b8c816b8110a176c22e67de9e07aaaa0a26547c2
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     hide: function() {
921        
922      
923         this.el.hide();   
924     },
925     show: function() {
926        
927         this.el.show();   
928     },
929     setWeight : function(str)
930     {
931         this.el.removeClass(this.weightClass);
932         this.el.addClass('btn-' + str);        
933     }
934     
935     
936 });
937
938  /*
939  * - LGPL
940  *
941  * column
942  * 
943  */
944
945 /**
946  * @class Roo.bootstrap.Column
947  * @extends Roo.bootstrap.Component
948  * Bootstrap Column class
949  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
957  *
958  * 
959  * @cfg {Boolean} hidden (true|false) hide the element
960  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961  * @cfg {String} fa (ban|check|...) font awesome icon
962  * @cfg {Number} fasize (1|2|....) font awsome size
963
964  * @cfg {String} icon (info-sign|check|...) glyphicon name
965
966  * @cfg {String} html content of column.
967  * 
968  * @constructor
969  * Create a new Column
970  * @param {Object} config The config object
971  */
972
973 Roo.bootstrap.Column = function(config){
974     Roo.bootstrap.Column.superclass.constructor.call(this, config);
975 };
976
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
978     
979     xs: false,
980     sm: false,
981     md: false,
982     lg: false,
983     xsoff: false,
984     smoff: false,
985     mdoff: false,
986     lgoff: false,
987     html: '',
988     offset: 0,
989     alert: false,
990     fa: false,
991     icon : false,
992     hidden : false,
993     fasize : 1,
994     
995     getAutoCreate : function(){
996         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
997         
998         cfg = {
999             tag: 'div',
1000             cls: 'column'
1001         };
1002         
1003         var settings=this;
1004         ['xs','sm','md','lg'].map(function(size){
1005             //Roo.log( size + ':' + settings[size]);
1006             
1007             if (settings[size+'off'] !== false) {
1008                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1009             }
1010             
1011             if (settings[size] === false) {
1012                 return;
1013             }
1014             
1015             if (!settings[size]) { // 0 = hidden
1016                 cfg.cls += ' hidden-' + size;
1017                 return;
1018             }
1019             cfg.cls += ' col-' + size + '-' + settings[size];
1020             
1021         });
1022         
1023         if (this.hidden) {
1024             cfg.cls += ' hidden';
1025         }
1026         
1027         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028             cfg.cls +=' alert alert-' + this.alert;
1029         }
1030         
1031         
1032         if (this.html.length) {
1033             cfg.html = this.html;
1034         }
1035         if (this.fa) {
1036             var fasize = '';
1037             if (this.fasize > 1) {
1038                 fasize = ' fa-' + this.fasize + 'x';
1039             }
1040             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1041             
1042             
1043         }
1044         if (this.icon) {
1045             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1046         }
1047         
1048         return cfg;
1049     }
1050    
1051 });
1052
1053  
1054
1055  /*
1056  * - LGPL
1057  *
1058  * page container.
1059  * 
1060  */
1061
1062
1063 /**
1064  * @class Roo.bootstrap.Container
1065  * @extends Roo.bootstrap.Component
1066  * Bootstrap Container class
1067  * @cfg {Boolean} jumbotron is it a jumbotron element
1068  * @cfg {String} html content of element
1069  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1071  * @cfg {String} header content of header (for panel)
1072  * @cfg {String} footer content of footer (for panel)
1073  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074  * @cfg {String} tag (header|aside|section) type of HTML tag.
1075  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076  * @cfg {String} fa font awesome icon
1077  * @cfg {String} icon (info-sign|check|...) glyphicon name
1078  * @cfg {Boolean} hidden (true|false) hide the element
1079  * @cfg {Boolean} expandable (true|false) default false
1080  * @cfg {Boolean} expanded (true|false) default true
1081  * @cfg {String} rheader contet on the right of header
1082  * @cfg {Boolean} clickable (true|false) default false
1083
1084  *     
1085  * @constructor
1086  * Create a new Container
1087  * @param {Object} config The config object
1088  */
1089
1090 Roo.bootstrap.Container = function(config){
1091     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1092     
1093     this.addEvents({
1094         // raw events
1095          /**
1096          * @event expand
1097          * After the panel has been expand
1098          * 
1099          * @param {Roo.bootstrap.Container} this
1100          */
1101         "expand" : true,
1102         /**
1103          * @event collapse
1104          * After the panel has been collapsed
1105          * 
1106          * @param {Roo.bootstrap.Container} this
1107          */
1108         "collapse" : true,
1109         /**
1110          * @event click
1111          * When a element is chick
1112          * @param {Roo.bootstrap.Container} this
1113          * @param {Roo.EventObject} e
1114          */
1115         "click" : true
1116     });
1117 };
1118
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1120     
1121     jumbotron : false,
1122     well: '',
1123     panel : '',
1124     header: '',
1125     footer : '',
1126     sticky: '',
1127     tag : false,
1128     alert : false,
1129     fa: false,
1130     icon : false,
1131     expandable : false,
1132     rheader : '',
1133     expanded : true,
1134     clickable: false,
1135   
1136      
1137     getChildContainer : function() {
1138         
1139         if(!this.el){
1140             return false;
1141         }
1142         
1143         if (this.panel.length) {
1144             return this.el.select('.panel-body',true).first();
1145         }
1146         
1147         return this.el;
1148     },
1149     
1150     
1151     getAutoCreate : function(){
1152         
1153         var cfg = {
1154             tag : this.tag || 'div',
1155             html : '',
1156             cls : ''
1157         };
1158         if (this.jumbotron) {
1159             cfg.cls = 'jumbotron';
1160         }
1161         
1162         
1163         
1164         // - this is applied by the parent..
1165         //if (this.cls) {
1166         //    cfg.cls = this.cls + '';
1167         //}
1168         
1169         if (this.sticky.length) {
1170             
1171             var bd = Roo.get(document.body);
1172             if (!bd.hasClass('bootstrap-sticky')) {
1173                 bd.addClass('bootstrap-sticky');
1174                 Roo.select('html',true).setStyle('height', '100%');
1175             }
1176              
1177             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1178         }
1179         
1180         
1181         if (this.well.length) {
1182             switch (this.well) {
1183                 case 'lg':
1184                 case 'sm':
1185                     cfg.cls +=' well well-' +this.well;
1186                     break;
1187                 default:
1188                     cfg.cls +=' well';
1189                     break;
1190             }
1191         }
1192         
1193         if (this.hidden) {
1194             cfg.cls += ' hidden';
1195         }
1196         
1197         
1198         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199             cfg.cls +=' alert alert-' + this.alert;
1200         }
1201         
1202         var body = cfg;
1203         
1204         if (this.panel.length) {
1205             cfg.cls += ' panel panel-' + this.panel;
1206             cfg.cn = [];
1207             if (this.header.length) {
1208                 
1209                 var h = [];
1210                 
1211                 if(this.expandable){
1212                     
1213                     cfg.cls = cfg.cls + ' expandable';
1214                     
1215                     h.push({
1216                         tag: 'i',
1217                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1218                     });
1219                     
1220                 }
1221                 
1222                 h.push(
1223                     {
1224                         tag: 'span',
1225                         cls : 'panel-title',
1226                         html : (this.expandable ? '&nbsp;' : '') + this.header
1227                     },
1228                     {
1229                         tag: 'span',
1230                         cls: 'panel-header-right',
1231                         html: this.rheader
1232                     }
1233                 );
1234                 
1235                 cfg.cn.push({
1236                     cls : 'panel-heading',
1237                     style : this.expandable ? 'cursor: pointer' : '',
1238                     cn : h
1239                 });
1240                 
1241             }
1242             
1243             body = false;
1244             cfg.cn.push({
1245                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1246                 html : this.html
1247             });
1248             
1249             
1250             if (this.footer.length) {
1251                 cfg.cn.push({
1252                     cls : 'panel-footer',
1253                     html : this.footer
1254                     
1255                 });
1256             }
1257             
1258         }
1259         
1260         if (body) {
1261             body.html = this.html || cfg.html;
1262             // prefix with the icons..
1263             if (this.fa) {
1264                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1265             }
1266             if (this.icon) {
1267                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1268             }
1269             
1270             
1271         }
1272         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273             cfg.cls =  'container';
1274         }
1275         
1276         return cfg;
1277     },
1278     
1279     initEvents: function() 
1280     {
1281         if(this.expandable){
1282             var headerEl = this.headerEl();
1283         
1284             if(headerEl){
1285                 headerEl.on('click', this.onToggleClick, this);
1286             }
1287         }
1288         
1289         if(this.clickable){
1290             this.el.on('click', this.onClick, this);
1291         }
1292         
1293     },
1294     
1295     onToggleClick : function()
1296     {
1297         var headerEl = this.headerEl();
1298         
1299         if(!headerEl){
1300             return;
1301         }
1302         
1303         if(this.expanded){
1304             this.collapse();
1305             return;
1306         }
1307         
1308         this.expand();
1309     },
1310     
1311     expand : function()
1312     {
1313         if(this.fireEvent('expand', this)) {
1314             
1315             this.expanded = true;
1316             
1317             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1318             
1319             this.el.select('.panel-body',true).first().removeClass('hide');
1320             
1321             var toggleEl = this.toggleEl();
1322
1323             if(!toggleEl){
1324                 return;
1325             }
1326
1327             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1328         }
1329         
1330     },
1331     
1332     collapse : function()
1333     {
1334         if(this.fireEvent('collapse', this)) {
1335             
1336             this.expanded = false;
1337             
1338             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339             this.el.select('.panel-body',true).first().addClass('hide');
1340         
1341             var toggleEl = this.toggleEl();
1342
1343             if(!toggleEl){
1344                 return;
1345             }
1346
1347             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1348         }
1349     },
1350     
1351     toggleEl : function()
1352     {
1353         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1354             return;
1355         }
1356         
1357         return this.el.select('.panel-heading .fa',true).first();
1358     },
1359     
1360     headerEl : function()
1361     {
1362         if(!this.el || !this.panel.length || !this.header.length){
1363             return;
1364         }
1365         
1366         return this.el.select('.panel-heading',true).first()
1367     },
1368     
1369     bodyEl : function()
1370     {
1371         if(!this.el || !this.panel.length){
1372             return;
1373         }
1374         
1375         return this.el.select('.panel-body',true).first()
1376     },
1377     
1378     titleEl : function()
1379     {
1380         if(!this.el || !this.panel.length || !this.header.length){
1381             return;
1382         }
1383         
1384         return this.el.select('.panel-title',true).first();
1385     },
1386     
1387     setTitle : function(v)
1388     {
1389         var titleEl = this.titleEl();
1390         
1391         if(!titleEl){
1392             return;
1393         }
1394         
1395         titleEl.dom.innerHTML = v;
1396     },
1397     
1398     getTitle : function()
1399     {
1400         
1401         var titleEl = this.titleEl();
1402         
1403         if(!titleEl){
1404             return '';
1405         }
1406         
1407         return titleEl.dom.innerHTML;
1408     },
1409     
1410     setRightTitle : function(v)
1411     {
1412         var t = this.el.select('.panel-header-right',true).first();
1413         
1414         if(!t){
1415             return;
1416         }
1417         
1418         t.dom.innerHTML = v;
1419     },
1420     
1421     onClick : function(e)
1422     {
1423         e.preventDefault();
1424         
1425         this.fireEvent('click', this, e);
1426     }
1427 });
1428
1429  /*
1430  * - LGPL
1431  *
1432  * image
1433  * 
1434  */
1435
1436
1437 /**
1438  * @class Roo.bootstrap.Img
1439  * @extends Roo.bootstrap.Component
1440  * Bootstrap Img class
1441  * @cfg {Boolean} imgResponsive false | true
1442  * @cfg {String} border rounded | circle | thumbnail
1443  * @cfg {String} src image source
1444  * @cfg {String} alt image alternative text
1445  * @cfg {String} href a tag href
1446  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447  * @cfg {String} xsUrl xs image source
1448  * @cfg {String} smUrl sm image source
1449  * @cfg {String} mdUrl md image source
1450  * @cfg {String} lgUrl lg image source
1451  * 
1452  * @constructor
1453  * Create a new Input
1454  * @param {Object} config The config object
1455  */
1456
1457 Roo.bootstrap.Img = function(config){
1458     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1459     
1460     this.addEvents({
1461         // img events
1462         /**
1463          * @event click
1464          * The img click event for the img.
1465          * @param {Roo.EventObject} e
1466          */
1467         "click" : true
1468     });
1469 };
1470
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1472     
1473     imgResponsive: true,
1474     border: '',
1475     src: 'about:blank',
1476     href: false,
1477     target: false,
1478     xsUrl: '',
1479     smUrl: '',
1480     mdUrl: '',
1481     lgUrl: '',
1482
1483     getAutoCreate : function()
1484     {   
1485         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486             return this.createSingleImg();
1487         }
1488         
1489         var cfg = {
1490             tag: 'div',
1491             cls: 'roo-image-responsive-group',
1492             cn: []
1493         };
1494         var _this = this;
1495         
1496         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1497             
1498             if(!_this[size + 'Url']){
1499                 return;
1500             }
1501             
1502             var img = {
1503                 tag: 'img',
1504                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505                 html: _this.html || cfg.html,
1506                 src: _this[size + 'Url']
1507             };
1508             
1509             img.cls += ' roo-image-responsive-' + size;
1510             
1511             var s = ['xs', 'sm', 'md', 'lg'];
1512             
1513             s.splice(s.indexOf(size), 1);
1514             
1515             Roo.each(s, function(ss){
1516                 img.cls += ' hidden-' + ss;
1517             });
1518             
1519             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520                 cfg.cls += ' img-' + _this.border;
1521             }
1522             
1523             if(_this.alt){
1524                 cfg.alt = _this.alt;
1525             }
1526             
1527             if(_this.href){
1528                 var a = {
1529                     tag: 'a',
1530                     href: _this.href,
1531                     cn: [
1532                         img
1533                     ]
1534                 };
1535
1536                 if(this.target){
1537                     a.target = _this.target;
1538                 }
1539             }
1540             
1541             cfg.cn.push((_this.href) ? a : img);
1542             
1543         });
1544         
1545         return cfg;
1546     },
1547     
1548     createSingleImg : function()
1549     {
1550         var cfg = {
1551             tag: 'img',
1552             cls: (this.imgResponsive) ? 'img-responsive' : '',
1553             html : null,
1554             src : 'about:blank'  // just incase src get's set to undefined?!?
1555         };
1556         
1557         cfg.html = this.html || cfg.html;
1558         
1559         cfg.src = this.src || cfg.src;
1560         
1561         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562             cfg.cls += ' img-' + this.border;
1563         }
1564         
1565         if(this.alt){
1566             cfg.alt = this.alt;
1567         }
1568         
1569         if(this.href){
1570             var a = {
1571                 tag: 'a',
1572                 href: this.href,
1573                 cn: [
1574                     cfg
1575                 ]
1576             };
1577             
1578             if(this.target){
1579                 a.target = this.target;
1580             }
1581             
1582         }
1583         
1584         return (this.href) ? a : cfg;
1585     },
1586     
1587     initEvents: function() 
1588     {
1589         if(!this.href){
1590             this.el.on('click', this.onClick, this);
1591         }
1592         
1593     },
1594     
1595     onClick : function(e)
1596     {
1597         Roo.log('img onclick');
1598         this.fireEvent('click', this, e);
1599     },
1600     /**
1601      * Sets the url of the image - used to update it
1602      * @param {String} url the url of the image
1603      */
1604     
1605     setSrc : function(url)
1606     {
1607         this.src =  url;
1608         
1609         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610             this.el.dom.src =  url;
1611             return;
1612         }
1613         
1614         this.el.select('img', true).first().dom.src =  url;
1615     }
1616     
1617     
1618    
1619 });
1620
1621  /*
1622  * - LGPL
1623  *
1624  * image
1625  * 
1626  */
1627
1628
1629 /**
1630  * @class Roo.bootstrap.Link
1631  * @extends Roo.bootstrap.Component
1632  * Bootstrap Link Class
1633  * @cfg {String} alt image alternative text
1634  * @cfg {String} href a tag href
1635  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636  * @cfg {String} html the content of the link.
1637  * @cfg {String} anchor name for the anchor link
1638  * @cfg {String} fa - favicon
1639
1640  * @cfg {Boolean} preventDefault (true | false) default false
1641
1642  * 
1643  * @constructor
1644  * Create a new Input
1645  * @param {Object} config The config object
1646  */
1647
1648 Roo.bootstrap.Link = function(config){
1649     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1650     
1651     this.addEvents({
1652         // img events
1653         /**
1654          * @event click
1655          * The img click event for the img.
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1663     
1664     href: false,
1665     target: false,
1666     preventDefault: false,
1667     anchor : false,
1668     alt : false,
1669     fa: false,
1670
1671
1672     getAutoCreate : function()
1673     {
1674         var html = this.html || '';
1675         
1676         if (this.fa !== false) {
1677             html = '<i class="fa fa-' + this.fa + '"></i>';
1678         }
1679         var cfg = {
1680             tag: 'a'
1681         };
1682         // anchor's do not require html/href...
1683         if (this.anchor === false) {
1684             cfg.html = html;
1685             cfg.href = this.href || '#';
1686         } else {
1687             cfg.name = this.anchor;
1688             if (this.html !== false || this.fa !== false) {
1689                 cfg.html = html;
1690             }
1691             if (this.href !== false) {
1692                 cfg.href = this.href;
1693             }
1694         }
1695         
1696         if(this.alt !== false){
1697             cfg.alt = this.alt;
1698         }
1699         
1700         
1701         if(this.target !== false) {
1702             cfg.target = this.target;
1703         }
1704         
1705         return cfg;
1706     },
1707     
1708     initEvents: function() {
1709         
1710         if(!this.href || this.preventDefault){
1711             this.el.on('click', this.onClick, this);
1712         }
1713     },
1714     
1715     onClick : function(e)
1716     {
1717         if(this.preventDefault){
1718             e.preventDefault();
1719         }
1720         //Roo.log('img onclick');
1721         this.fireEvent('click', this, e);
1722     }
1723    
1724 });
1725
1726  /*
1727  * - LGPL
1728  *
1729  * header
1730  * 
1731  */
1732
1733 /**
1734  * @class Roo.bootstrap.Header
1735  * @extends Roo.bootstrap.Component
1736  * Bootstrap Header class
1737  * @cfg {String} html content of header
1738  * @cfg {Number} level (1|2|3|4|5|6) default 1
1739  * 
1740  * @constructor
1741  * Create a new Header
1742  * @param {Object} config The config object
1743  */
1744
1745
1746 Roo.bootstrap.Header  = function(config){
1747     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1748 };
1749
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1751     
1752     //href : false,
1753     html : false,
1754     level : 1,
1755     
1756     
1757     
1758     getAutoCreate : function(){
1759         
1760         
1761         
1762         var cfg = {
1763             tag: 'h' + (1 *this.level),
1764             html: this.html || ''
1765         } ;
1766         
1767         return cfg;
1768     }
1769    
1770 });
1771
1772  
1773
1774  /*
1775  * Based on:
1776  * Ext JS Library 1.1.1
1777  * Copyright(c) 2006-2007, Ext JS, LLC.
1778  *
1779  * Originally Released Under LGPL - original licence link has changed is not relivant.
1780  *
1781  * Fork - LGPL
1782  * <script type="text/javascript">
1783  */
1784  
1785 /**
1786  * @class Roo.bootstrap.MenuMgr
1787  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1788  * @singleton
1789  */
1790 Roo.bootstrap.MenuMgr = function(){
1791    var menus, active, groups = {}, attached = false, lastShow = new Date();
1792
1793    // private - called when first menu is created
1794    function init(){
1795        menus = {};
1796        active = new Roo.util.MixedCollection();
1797        Roo.get(document).addKeyListener(27, function(){
1798            if(active.length > 0){
1799                hideAll();
1800            }
1801        });
1802    }
1803
1804    // private
1805    function hideAll(){
1806        if(active && active.length > 0){
1807            var c = active.clone();
1808            c.each(function(m){
1809                m.hide();
1810            });
1811        }
1812    }
1813
1814    // private
1815    function onHide(m){
1816        active.remove(m);
1817        if(active.length < 1){
1818            Roo.get(document).un("mouseup", onMouseDown);
1819             
1820            attached = false;
1821        }
1822    }
1823
1824    // private
1825    function onShow(m){
1826        var last = active.last();
1827        lastShow = new Date();
1828        active.add(m);
1829        if(!attached){
1830           Roo.get(document).on("mouseup", onMouseDown);
1831            
1832            attached = true;
1833        }
1834        if(m.parentMenu){
1835           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836           m.parentMenu.activeChild = m;
1837        }else if(last && last.isVisible()){
1838           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1839        }
1840    }
1841
1842    // private
1843    function onBeforeHide(m){
1844        if(m.activeChild){
1845            m.activeChild.hide();
1846        }
1847        if(m.autoHideTimer){
1848            clearTimeout(m.autoHideTimer);
1849            delete m.autoHideTimer;
1850        }
1851    }
1852
1853    // private
1854    function onBeforeShow(m){
1855        var pm = m.parentMenu;
1856        if(!pm && !m.allowOtherMenus){
1857            hideAll();
1858        }else if(pm && pm.activeChild && active != m){
1859            pm.activeChild.hide();
1860        }
1861    }
1862
1863    // private this should really trigger on mouseup..
1864    function onMouseDown(e){
1865         Roo.log("on Mouse Up");
1866         
1867         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868             Roo.log("MenuManager hideAll");
1869             hideAll();
1870             e.stopEvent();
1871         }
1872         
1873         
1874    }
1875
1876    // private
1877    function onBeforeCheck(mi, state){
1878        if(state){
1879            var g = groups[mi.group];
1880            for(var i = 0, l = g.length; i < l; i++){
1881                if(g[i] != mi){
1882                    g[i].setChecked(false);
1883                }
1884            }
1885        }
1886    }
1887
1888    return {
1889
1890        /**
1891         * Hides all menus that are currently visible
1892         */
1893        hideAll : function(){
1894             hideAll();  
1895        },
1896
1897        // private
1898        register : function(menu){
1899            if(!menus){
1900                init();
1901            }
1902            menus[menu.id] = menu;
1903            menu.on("beforehide", onBeforeHide);
1904            menu.on("hide", onHide);
1905            menu.on("beforeshow", onBeforeShow);
1906            menu.on("show", onShow);
1907            var g = menu.group;
1908            if(g && menu.events["checkchange"]){
1909                if(!groups[g]){
1910                    groups[g] = [];
1911                }
1912                groups[g].push(menu);
1913                menu.on("checkchange", onCheck);
1914            }
1915        },
1916
1917         /**
1918          * Returns a {@link Roo.menu.Menu} object
1919          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920          * be used to generate and return a new Menu instance.
1921          */
1922        get : function(menu){
1923            if(typeof menu == "string"){ // menu id
1924                return menus[menu];
1925            }else if(menu.events){  // menu instance
1926                return menu;
1927            }
1928            /*else if(typeof menu.length == 'number'){ // array of menu items?
1929                return new Roo.bootstrap.Menu({items:menu});
1930            }else{ // otherwise, must be a config
1931                return new Roo.bootstrap.Menu(menu);
1932            }
1933            */
1934            return false;
1935        },
1936
1937        // private
1938        unregister : function(menu){
1939            delete menus[menu.id];
1940            menu.un("beforehide", onBeforeHide);
1941            menu.un("hide", onHide);
1942            menu.un("beforeshow", onBeforeShow);
1943            menu.un("show", onShow);
1944            var g = menu.group;
1945            if(g && menu.events["checkchange"]){
1946                groups[g].remove(menu);
1947                menu.un("checkchange", onCheck);
1948            }
1949        },
1950
1951        // private
1952        registerCheckable : function(menuItem){
1953            var g = menuItem.group;
1954            if(g){
1955                if(!groups[g]){
1956                    groups[g] = [];
1957                }
1958                groups[g].push(menuItem);
1959                menuItem.on("beforecheckchange", onBeforeCheck);
1960            }
1961        },
1962
1963        // private
1964        unregisterCheckable : function(menuItem){
1965            var g = menuItem.group;
1966            if(g){
1967                groups[g].remove(menuItem);
1968                menuItem.un("beforecheckchange", onBeforeCheck);
1969            }
1970        }
1971    };
1972 }();/*
1973  * - LGPL
1974  *
1975  * menu
1976  * 
1977  */
1978
1979 /**
1980  * @class Roo.bootstrap.Menu
1981  * @extends Roo.bootstrap.Component
1982  * Bootstrap Menu class - container for MenuItems
1983  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1985  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1986  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1987  * 
1988  * @constructor
1989  * Create a new Menu
1990  * @param {Object} config The config object
1991  */
1992
1993
1994 Roo.bootstrap.Menu = function(config){
1995     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996     if (this.registerMenu && this.type != 'treeview')  {
1997         Roo.bootstrap.MenuMgr.register(this);
1998     }
1999     this.addEvents({
2000         /**
2001          * @event beforeshow
2002          * Fires before this menu is displayed
2003          * @param {Roo.menu.Menu} this
2004          */
2005         beforeshow : true,
2006         /**
2007          * @event beforehide
2008          * Fires before this menu is hidden
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforehide : true,
2012         /**
2013          * @event show
2014          * Fires after this menu is displayed
2015          * @param {Roo.menu.Menu} this
2016          */
2017         show : true,
2018         /**
2019          * @event hide
2020          * Fires after this menu is hidden
2021          * @param {Roo.menu.Menu} this
2022          */
2023         hide : true,
2024         /**
2025          * @event click
2026          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029          * @param {Roo.EventObject} e
2030          */
2031         click : true,
2032         /**
2033          * @event mouseover
2034          * Fires when the mouse is hovering over this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseover : true,
2040         /**
2041          * @event mouseout
2042          * Fires when the mouse exits this menu
2043          * @param {Roo.menu.Menu} this
2044          * @param {Roo.EventObject} e
2045          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2046          */
2047         mouseout : true,
2048         /**
2049          * @event itemclick
2050          * Fires when a menu item contained in this menu is clicked
2051          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052          * @param {Roo.EventObject} e
2053          */
2054         itemclick: true
2055     });
2056     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2057 };
2058
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2060     
2061    /// html : false,
2062     //align : '',
2063     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2064     type: false,
2065     /**
2066      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2067      */
2068     registerMenu : true,
2069     
2070     menuItems :false, // stores the menu items..
2071     
2072     hidden:true,
2073         
2074     parentMenu : false,
2075     
2076     stopEvent : true,
2077     
2078     isLink : false,
2079     
2080     getChildContainer : function() {
2081         return this.el;  
2082     },
2083     
2084     getAutoCreate : function(){
2085          
2086         //if (['right'].indexOf(this.align)!==-1) {
2087         //    cfg.cn[1].cls += ' pull-right'
2088         //}
2089         
2090         
2091         var cfg = {
2092             tag : 'ul',
2093             cls : 'dropdown-menu' ,
2094             style : 'z-index:1000'
2095             
2096         };
2097         
2098         if (this.type === 'submenu') {
2099             cfg.cls = 'submenu active';
2100         }
2101         if (this.type === 'treeview') {
2102             cfg.cls = 'treeview-menu';
2103         }
2104         
2105         return cfg;
2106     },
2107     initEvents : function() {
2108         
2109        // Roo.log("ADD event");
2110        // Roo.log(this.triggerEl.dom);
2111         
2112         this.triggerEl.on('click', this.onTriggerClick, this);
2113         
2114         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2115         
2116         this.triggerEl.addClass('dropdown-toggle');
2117         
2118         if (Roo.isTouch) {
2119             this.el.on('touchstart'  , this.onTouch, this);
2120         }
2121         this.el.on('click' , this.onClick, this);
2122
2123         this.el.on("mouseover", this.onMouseOver, this);
2124         this.el.on("mouseout", this.onMouseOut, this);
2125         
2126     },
2127     
2128     findTargetItem : function(e)
2129     {
2130         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2131         if(!t){
2132             return false;
2133         }
2134         //Roo.log(t);         Roo.log(t.id);
2135         if(t && t.id){
2136             //Roo.log(this.menuitems);
2137             return this.menuitems.get(t.id);
2138             
2139             //return this.items.get(t.menuItemId);
2140         }
2141         
2142         return false;
2143     },
2144     
2145     onTouch : function(e) 
2146     {
2147         Roo.log("menu.onTouch");
2148         //e.stopEvent(); this make the user popdown broken
2149         this.onClick(e);
2150     },
2151     
2152     onClick : function(e)
2153     {
2154         Roo.log("menu.onClick");
2155         
2156         var t = this.findTargetItem(e);
2157         if(!t || t.isContainer){
2158             return;
2159         }
2160         Roo.log(e);
2161         /*
2162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2163             if(t == this.activeItem && t.shouldDeactivate(e)){
2164                 this.activeItem.deactivate();
2165                 delete this.activeItem;
2166                 return;
2167             }
2168             if(t.canActivate){
2169                 this.setActiveItem(t, true);
2170             }
2171             return;
2172             
2173             
2174         }
2175         */
2176        
2177         Roo.log('pass click event');
2178         
2179         t.onClick(e);
2180         
2181         this.fireEvent("click", this, t, e);
2182         
2183         var _this = this;
2184         
2185         if(!t.href.length || t.href == '#'){
2186             (function() { _this.hide(); }).defer(100);
2187         }
2188         
2189     },
2190     
2191     onMouseOver : function(e){
2192         var t  = this.findTargetItem(e);
2193         //Roo.log(t);
2194         //if(t){
2195         //    if(t.canActivate && !t.disabled){
2196         //        this.setActiveItem(t, true);
2197         //    }
2198         //}
2199         
2200         this.fireEvent("mouseover", this, e, t);
2201     },
2202     isVisible : function(){
2203         return !this.hidden;
2204     },
2205      onMouseOut : function(e){
2206         var t  = this.findTargetItem(e);
2207         
2208         //if(t ){
2209         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2210         //        this.activeItem.deactivate();
2211         //        delete this.activeItem;
2212         //    }
2213         //}
2214         this.fireEvent("mouseout", this, e, t);
2215     },
2216     
2217     
2218     /**
2219      * Displays this menu relative to another element
2220      * @param {String/HTMLElement/Roo.Element} element The element to align to
2221      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222      * the element (defaults to this.defaultAlign)
2223      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2224      */
2225     show : function(el, pos, parentMenu){
2226         this.parentMenu = parentMenu;
2227         if(!this.el){
2228             this.render();
2229         }
2230         this.fireEvent("beforeshow", this);
2231         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2232     },
2233      /**
2234      * Displays this menu at a specific xy position
2235      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     showAt : function(xy, parentMenu, /* private: */_e){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         if(_e !== false){
2244             this.fireEvent("beforeshow", this);
2245             //xy = this.el.adjustForConstraints(xy);
2246         }
2247         
2248         //this.el.show();
2249         this.hideMenuItems();
2250         this.hidden = false;
2251         this.triggerEl.addClass('open');
2252         
2253         // reassign x when hitting right
2254         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2256         }
2257         
2258         // reassign y when hitting bottom
2259         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2261         }
2262         
2263         // but the list may align on trigger left or trigger top... should it be a properity?
2264         
2265         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2266             this.el.setXY(xy);
2267         }
2268         
2269         this.focus();
2270         this.fireEvent("show", this);
2271     },
2272     
2273     focus : function(){
2274         return;
2275         if(!this.hidden){
2276             this.doFocus.defer(50, this);
2277         }
2278     },
2279
2280     doFocus : function(){
2281         if(!this.hidden){
2282             this.focusEl.focus();
2283         }
2284     },
2285
2286     /**
2287      * Hides this menu and optionally all parent menus
2288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2289      */
2290     hide : function(deep)
2291     {
2292         
2293         this.hideMenuItems();
2294         if(this.el && this.isVisible()){
2295             this.fireEvent("beforehide", this);
2296             if(this.activeItem){
2297                 this.activeItem.deactivate();
2298                 this.activeItem = null;
2299             }
2300             this.triggerEl.removeClass('open');;
2301             this.hidden = true;
2302             this.fireEvent("hide", this);
2303         }
2304         if(deep === true && this.parentMenu){
2305             this.parentMenu.hide(true);
2306         }
2307     },
2308     
2309     onTriggerClick : function(e)
2310     {
2311         Roo.log('trigger click');
2312         
2313         var target = e.getTarget();
2314         
2315         Roo.log(target.nodeName.toLowerCase());
2316         
2317         if(target.nodeName.toLowerCase() === 'i'){
2318             e.preventDefault();
2319         }
2320         
2321     },
2322     
2323     onTriggerPress  : function(e)
2324     {
2325         Roo.log('trigger press');
2326         //Roo.log(e.getTarget());
2327        // Roo.log(this.triggerEl.dom);
2328        
2329         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330         var pel = Roo.get(e.getTarget());
2331         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332             Roo.log('is treeview or dropdown?');
2333             return;
2334         }
2335         
2336         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2337             return;
2338         }
2339         
2340         if (this.isVisible()) {
2341             Roo.log('hide');
2342             this.hide();
2343         } else {
2344             Roo.log('show');
2345             this.show(this.triggerEl, false, false);
2346         }
2347         
2348         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2349             e.stopEvent();
2350         }
2351         
2352     },
2353        
2354     
2355     hideMenuItems : function()
2356     {
2357         Roo.log("hide Menu Items");
2358         if (!this.el) { 
2359             return;
2360         }
2361         //$(backdrop).remove()
2362         this.el.select('.open',true).each(function(aa) {
2363             
2364             aa.removeClass('open');
2365           //var parent = getParent($(this))
2366           //var relatedTarget = { relatedTarget: this }
2367           
2368            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369           //if (e.isDefaultPrevented()) return
2370            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2371         });
2372     },
2373     addxtypeChild : function (tree, cntr) {
2374         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2375           
2376         this.menuitems.add(comp);
2377         return comp;
2378
2379     },
2380     getEl : function()
2381     {
2382         Roo.log(this.el);
2383         return this.el;
2384     },
2385     
2386     clear : function()
2387     {
2388         this.getEl().dom.innerHTML = '';
2389         this.menuitems.clear();
2390     }
2391 });
2392
2393  
2394  /*
2395  * - LGPL
2396  *
2397  * menu item
2398  * 
2399  */
2400
2401
2402 /**
2403  * @class Roo.bootstrap.MenuItem
2404  * @extends Roo.bootstrap.Component
2405  * Bootstrap MenuItem class
2406  * @cfg {String} html the menu label
2407  * @cfg {String} href the link
2408  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2411  * @cfg {String} fa favicon to show on left of menu item.
2412  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2413  * 
2414  * 
2415  * @constructor
2416  * Create a new MenuItem
2417  * @param {Object} config The config object
2418  */
2419
2420
2421 Roo.bootstrap.MenuItem = function(config){
2422     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2423     this.addEvents({
2424         // raw events
2425         /**
2426          * @event click
2427          * The raw click event for the entire grid.
2428          * @param {Roo.bootstrap.MenuItem} this
2429          * @param {Roo.EventObject} e
2430          */
2431         "click" : true
2432     });
2433 };
2434
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2436     
2437     href : false,
2438     html : false,
2439     preventDefault: false,
2440     isContainer : false,
2441     active : false,
2442     fa: false,
2443     
2444     getAutoCreate : function(){
2445         
2446         if(this.isContainer){
2447             return {
2448                 tag: 'li',
2449                 cls: 'dropdown-menu-item'
2450             };
2451         }
2452         var ctag = {
2453             tag: 'span',
2454             html: 'Link'
2455         };
2456         
2457         var anc = {
2458             tag : 'a',
2459             href : '#',
2460             cn : [  ]
2461         };
2462         
2463         if (this.fa !== false) {
2464             anc.cn.push({
2465                 tag : 'i',
2466                 cls : 'fa fa-' + this.fa
2467             });
2468         }
2469         
2470         anc.cn.push(ctag);
2471         
2472         
2473         var cfg= {
2474             tag: 'li',
2475             cls: 'dropdown-menu-item',
2476             cn: [ anc ]
2477         };
2478         if (this.parent().type == 'treeview') {
2479             cfg.cls = 'treeview-menu';
2480         }
2481         if (this.active) {
2482             cfg.cls += ' active';
2483         }
2484         
2485         
2486         
2487         anc.href = this.href || cfg.cn[0].href ;
2488         ctag.html = this.html || cfg.cn[0].html ;
2489         return cfg;
2490     },
2491     
2492     initEvents: function()
2493     {
2494         if (this.parent().type == 'treeview') {
2495             this.el.select('a').on('click', this.onClick, this);
2496         }
2497         
2498         if (this.menu) {
2499             this.menu.parentType = this.xtype;
2500             this.menu.triggerEl = this.el;
2501             this.menu = this.addxtype(Roo.apply({}, this.menu));
2502         }
2503         
2504     },
2505     onClick : function(e)
2506     {
2507         Roo.log('item on click ');
2508         
2509         if(this.preventDefault){
2510             e.preventDefault();
2511         }
2512         //this.parent().hideMenuItems();
2513         
2514         this.fireEvent('click', this, e);
2515     },
2516     getEl : function()
2517     {
2518         return this.el;
2519     } 
2520 });
2521
2522  
2523
2524  /*
2525  * - LGPL
2526  *
2527  * menu separator
2528  * 
2529  */
2530
2531
2532 /**
2533  * @class Roo.bootstrap.MenuSeparator
2534  * @extends Roo.bootstrap.Component
2535  * Bootstrap MenuSeparator class
2536  * 
2537  * @constructor
2538  * Create a new MenuItem
2539  * @param {Object} config The config object
2540  */
2541
2542
2543 Roo.bootstrap.MenuSeparator = function(config){
2544     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2545 };
2546
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2548     
2549     getAutoCreate : function(){
2550         var cfg = {
2551             cls: 'divider',
2552             tag : 'li'
2553         };
2554         
2555         return cfg;
2556     }
2557    
2558 });
2559
2560  
2561
2562  
2563 /*
2564 * Licence: LGPL
2565 */
2566
2567 /**
2568  * @class Roo.bootstrap.Modal
2569  * @extends Roo.bootstrap.Component
2570  * Bootstrap Modal class
2571  * @cfg {String} title Title of dialog
2572  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2574  * @cfg {Boolean} specificTitle default false
2575  * @cfg {Array} buttons Array of buttons or standard button set..
2576  * @cfg {String} buttonPosition (left|right|center) default right
2577  * @cfg {Boolean} animate default true
2578  * @cfg {Boolean} allow_close default true
2579  * @cfg {Boolean} fitwindow default false
2580  * @cfg {String} size (sm|lg) default empty
2581  * @cfg {Number} max_width set the max width of modal
2582  *
2583  *
2584  * @constructor
2585  * Create a new Modal Dialog
2586  * @param {Object} config The config object
2587  */
2588
2589 Roo.bootstrap.Modal = function(config){
2590     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2591     this.addEvents({
2592         // raw events
2593         /**
2594          * @event btnclick
2595          * The raw btnclick event for the button
2596          * @param {Roo.EventObject} e
2597          */
2598         "btnclick" : true,
2599         /**
2600          * @event resize
2601          * Fire when dialog resize
2602          * @param {Roo.bootstrap.Modal} this
2603          * @param {Roo.EventObject} e
2604          */
2605         "resize" : true
2606     });
2607     this.buttons = this.buttons || [];
2608
2609     if (this.tmpl) {
2610         this.tmpl = Roo.factory(this.tmpl);
2611     }
2612
2613 };
2614
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2616
2617     title : 'test dialog',
2618
2619     buttons : false,
2620
2621     // set on load...
2622
2623     html: false,
2624
2625     tmp: false,
2626
2627     specificTitle: false,
2628
2629     buttonPosition: 'right',
2630
2631     allow_close : true,
2632
2633     animate : true,
2634
2635     fitwindow: false,
2636
2637
2638      // private
2639     dialogEl: false,
2640     bodyEl:  false,
2641     footerEl:  false,
2642     titleEl:  false,
2643     closeEl:  false,
2644
2645     size: '',
2646     
2647     max_width: 0,
2648     
2649     fit_content: false,
2650
2651     onRender : function(ct, position)
2652     {
2653         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2654
2655         if(!this.el){
2656             var cfg = Roo.apply({},  this.getAutoCreate());
2657             cfg.id = Roo.id();
2658             //if(!cfg.name){
2659             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660             //}
2661             //if (!cfg.name.length) {
2662             //    delete cfg.name;
2663            // }
2664             if (this.cls) {
2665                 cfg.cls += ' ' + this.cls;
2666             }
2667             if (this.style) {
2668                 cfg.style = this.style;
2669             }
2670             this.el = Roo.get(document.body).createChild(cfg, position);
2671         }
2672         //var type = this.el.dom.type;
2673
2674
2675         if(this.tabIndex !== undefined){
2676             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2677         }
2678
2679         this.dialogEl = this.el.select('.modal-dialog',true).first();
2680         this.bodyEl = this.el.select('.modal-body',true).first();
2681         this.closeEl = this.el.select('.modal-header .close', true).first();
2682         this.headerEl = this.el.select('.modal-header',true).first();
2683         this.titleEl = this.el.select('.modal-title',true).first();
2684         this.footerEl = this.el.select('.modal-footer',true).first();
2685
2686         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687         
2688         //this.el.addClass("x-dlg-modal");
2689
2690         if (this.buttons.length) {
2691             Roo.each(this.buttons, function(bb) {
2692                 var b = Roo.apply({}, bb);
2693                 b.xns = b.xns || Roo.bootstrap;
2694                 b.xtype = b.xtype || 'Button';
2695                 if (typeof(b.listeners) == 'undefined') {
2696                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2697                 }
2698
2699                 var btn = Roo.factory(b);
2700
2701                 btn.render(this.el.select('.modal-footer div').first());
2702
2703             },this);
2704         }
2705         // render the children.
2706         var nitems = [];
2707
2708         if(typeof(this.items) != 'undefined'){
2709             var items = this.items;
2710             delete this.items;
2711
2712             for(var i =0;i < items.length;i++) {
2713                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2714             }
2715         }
2716
2717         this.items = nitems;
2718
2719         // where are these used - they used to be body/close/footer
2720
2721
2722         this.initEvents();
2723         //this.el.addClass([this.fieldClass, this.cls]);
2724
2725     },
2726
2727     getAutoCreate : function()
2728     {
2729         var bdy = {
2730                 cls : 'modal-body',
2731                 html : this.html || ''
2732         };
2733
2734         var title = {
2735             tag: 'h4',
2736             cls : 'modal-title',
2737             html : this.title
2738         };
2739
2740         if(this.specificTitle){
2741             title = this.title;
2742
2743         };
2744
2745         var header = [];
2746         if (this.allow_close) {
2747             header.push({
2748                 tag: 'button',
2749                 cls : 'close',
2750                 html : '&times'
2751             });
2752         }
2753
2754         header.push(title);
2755
2756         var size = '';
2757
2758         if(this.size.length){
2759             size = 'modal-' + this.size;
2760         }
2761
2762         var modal = {
2763             cls: "modal",
2764              cn : [
2765                 {
2766                     cls: "modal-dialog " + size,
2767                     cn : [
2768                         {
2769                             cls : "modal-content",
2770                             cn : [
2771                                 {
2772                                     cls : 'modal-header',
2773                                     cn : header
2774                                 },
2775                                 bdy,
2776                                 {
2777                                     cls : 'modal-footer',
2778                                     cn : [
2779                                         {
2780                                             tag: 'div',
2781                                             cls: 'btn-' + this.buttonPosition
2782                                         }
2783                                     ]
2784
2785                                 }
2786
2787
2788                             ]
2789
2790                         }
2791                     ]
2792
2793                 }
2794             ]
2795         };
2796
2797         if(this.animate){
2798             modal.cls += ' fade';
2799         }
2800
2801         return modal;
2802
2803     },
2804     getChildContainer : function() {
2805
2806          return this.bodyEl;
2807
2808     },
2809     getButtonContainer : function() {
2810          return this.el.select('.modal-footer div',true).first();
2811
2812     },
2813     initEvents : function()
2814     {
2815         if (this.allow_close) {
2816             this.closeEl.on('click', this.hide, this);
2817         }
2818         Roo.EventManager.onWindowResize(this.resize, this, true);
2819
2820
2821     },
2822
2823     resize : function()
2824     {
2825         this.maskEl.setSize(
2826             Roo.lib.Dom.getViewWidth(true),
2827             Roo.lib.Dom.getViewHeight(true)
2828         );
2829         
2830         if (this.fitwindow) {
2831             this.setSize(
2832                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2834             );
2835             return;
2836         }
2837         
2838         if(this.max_width !== 0) {
2839             
2840             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2841             
2842             if(this.height) {
2843                 this.setSize(w, this.height);
2844                 return;
2845             }
2846             
2847             if(!this.fit_content) {
2848                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2849                 return;
2850             }
2851             
2852             var body_childs = this.bodyEl.dom.childNodes;
2853             // does not seem to give enough space...
2854             var full_height = 60 + this.headerEl.getHeight() + this.footerEl.getHeight();
2855             for(var i = 0; i < body_childs.length; i++) {
2856                 full_height += body_childs[i].offsetHeight;
2857             }
2858             
2859             this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2860         }
2861         
2862     },
2863
2864     setSize : function(w,h)
2865     {
2866         if (!w && !h) {
2867             return;
2868         }
2869         this.resizeTo(w,h);
2870     },
2871
2872     show : function() {
2873
2874         if (!this.rendered) {
2875             this.render();
2876         }
2877
2878         //this.el.setStyle('display', 'block');
2879         this.el.removeClass('hideing');        
2880         this.el.addClass('show');
2881  
2882         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2883             var _this = this;
2884             (function(){
2885                 this.el.addClass('in');
2886             }).defer(50, this);
2887         }else{
2888             this.el.addClass('in');
2889         }
2890
2891         // not sure how we can show data in here..
2892         //if (this.tmpl) {
2893         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2894         //}
2895
2896         Roo.get(document.body).addClass("x-body-masked");
2897         
2898         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2899         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2900         this.maskEl.addClass('show');
2901         
2902         this.resize();
2903         
2904         this.fireEvent('show', this);
2905
2906         // set zindex here - otherwise it appears to be ignored...
2907         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2908
2909         (function () {
2910             this.items.forEach( function(e) {
2911                 e.layout ? e.layout() : false;
2912
2913             });
2914         }).defer(100,this);
2915
2916     },
2917     hide : function()
2918     {
2919         if(this.fireEvent("beforehide", this) !== false){
2920             this.maskEl.removeClass('show');
2921             Roo.get(document.body).removeClass("x-body-masked");
2922             this.el.removeClass('in');
2923             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2924
2925             if(this.animate){ // why
2926                 this.el.addClass('hideing');
2927                 (function(){
2928                     if (!this.el.hasClass('hideing')) {
2929                         return; // it's been shown again...
2930                     }
2931                     this.el.removeClass('show');
2932                     this.el.removeClass('hideing');
2933                 }).defer(150,this);
2934                 
2935             }else{
2936                  this.el.removeClass('show');
2937             }
2938             this.fireEvent('hide', this);
2939         }
2940     },
2941     isVisible : function()
2942     {
2943         
2944         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2945         
2946     },
2947
2948     addButton : function(str, cb)
2949     {
2950
2951
2952         var b = Roo.apply({}, { html : str } );
2953         b.xns = b.xns || Roo.bootstrap;
2954         b.xtype = b.xtype || 'Button';
2955         if (typeof(b.listeners) == 'undefined') {
2956             b.listeners = { click : cb.createDelegate(this)  };
2957         }
2958
2959         var btn = Roo.factory(b);
2960
2961         btn.render(this.el.select('.modal-footer div').first());
2962
2963         return btn;
2964
2965     },
2966
2967     setDefaultButton : function(btn)
2968     {
2969         //this.el.select('.modal-footer').()
2970     },
2971     diff : false,
2972
2973     resizeTo: function(w,h)
2974     {
2975         // skip.. ?? why??
2976
2977         this.dialogEl.setWidth(w);
2978         if (this.diff === false) {
2979             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2980         }
2981
2982         this.bodyEl.setHeight(h-this.diff);
2983
2984         this.fireEvent('resize', this);
2985
2986     },
2987     setContentSize  : function(w, h)
2988     {
2989
2990     },
2991     onButtonClick: function(btn,e)
2992     {
2993         //Roo.log([a,b,c]);
2994         this.fireEvent('btnclick', btn.name, e);
2995     },
2996      /**
2997      * Set the title of the Dialog
2998      * @param {String} str new Title
2999      */
3000     setTitle: function(str) {
3001         this.titleEl.dom.innerHTML = str;
3002     },
3003     /**
3004      * Set the body of the Dialog
3005      * @param {String} str new Title
3006      */
3007     setBody: function(str) {
3008         this.bodyEl.dom.innerHTML = str;
3009     },
3010     /**
3011      * Set the body of the Dialog using the template
3012      * @param {Obj} data - apply this data to the template and replace the body contents.
3013      */
3014     applyBody: function(obj)
3015     {
3016         if (!this.tmpl) {
3017             Roo.log("Error - using apply Body without a template");
3018             //code
3019         }
3020         this.tmpl.overwrite(this.bodyEl, obj);
3021     }
3022
3023 });
3024
3025
3026 Roo.apply(Roo.bootstrap.Modal,  {
3027     /**
3028          * Button config that displays a single OK button
3029          * @type Object
3030          */
3031         OK :  [{
3032             name : 'ok',
3033             weight : 'primary',
3034             html : 'OK'
3035         }],
3036         /**
3037          * Button config that displays Yes and No buttons
3038          * @type Object
3039          */
3040         YESNO : [
3041             {
3042                 name  : 'no',
3043                 html : 'No'
3044             },
3045             {
3046                 name  :'yes',
3047                 weight : 'primary',
3048                 html : 'Yes'
3049             }
3050         ],
3051
3052         /**
3053          * Button config that displays OK and Cancel buttons
3054          * @type Object
3055          */
3056         OKCANCEL : [
3057             {
3058                name : 'cancel',
3059                 html : 'Cancel'
3060             },
3061             {
3062                 name : 'ok',
3063                 weight : 'primary',
3064                 html : 'OK'
3065             }
3066         ],
3067         /**
3068          * Button config that displays Yes, No and Cancel buttons
3069          * @type Object
3070          */
3071         YESNOCANCEL : [
3072             {
3073                 name : 'yes',
3074                 weight : 'primary',
3075                 html : 'Yes'
3076             },
3077             {
3078                 name : 'no',
3079                 html : 'No'
3080             },
3081             {
3082                 name : 'cancel',
3083                 html : 'Cancel'
3084             }
3085         ],
3086         
3087         zIndex : 10001
3088 });
3089 /*
3090  * - LGPL
3091  *
3092  * messagebox - can be used as a replace
3093  * 
3094  */
3095 /**
3096  * @class Roo.MessageBox
3097  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3098  * Example usage:
3099  *<pre><code>
3100 // Basic alert:
3101 Roo.Msg.alert('Status', 'Changes saved successfully.');
3102
3103 // Prompt for user data:
3104 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3105     if (btn == 'ok'){
3106         // process text value...
3107     }
3108 });
3109
3110 // Show a dialog using config options:
3111 Roo.Msg.show({
3112    title:'Save Changes?',
3113    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3114    buttons: Roo.Msg.YESNOCANCEL,
3115    fn: processResult,
3116    animEl: 'elId'
3117 });
3118 </code></pre>
3119  * @singleton
3120  */
3121 Roo.bootstrap.MessageBox = function(){
3122     var dlg, opt, mask, waitTimer;
3123     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3124     var buttons, activeTextEl, bwidth;
3125
3126     
3127     // private
3128     var handleButton = function(button){
3129         dlg.hide();
3130         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3131     };
3132
3133     // private
3134     var handleHide = function(){
3135         if(opt && opt.cls){
3136             dlg.el.removeClass(opt.cls);
3137         }
3138         //if(waitTimer){
3139         //    Roo.TaskMgr.stop(waitTimer);
3140         //    waitTimer = null;
3141         //}
3142     };
3143
3144     // private
3145     var updateButtons = function(b){
3146         var width = 0;
3147         if(!b){
3148             buttons["ok"].hide();
3149             buttons["cancel"].hide();
3150             buttons["yes"].hide();
3151             buttons["no"].hide();
3152             //dlg.footer.dom.style.display = 'none';
3153             return width;
3154         }
3155         dlg.footerEl.dom.style.display = '';
3156         for(var k in buttons){
3157             if(typeof buttons[k] != "function"){
3158                 if(b[k]){
3159                     buttons[k].show();
3160                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3161                     width += buttons[k].el.getWidth()+15;
3162                 }else{
3163                     buttons[k].hide();
3164                 }
3165             }
3166         }
3167         return width;
3168     };
3169
3170     // private
3171     var handleEsc = function(d, k, e){
3172         if(opt && opt.closable !== false){
3173             dlg.hide();
3174         }
3175         if(e){
3176             e.stopEvent();
3177         }
3178     };
3179
3180     return {
3181         /**
3182          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3183          * @return {Roo.BasicDialog} The BasicDialog element
3184          */
3185         getDialog : function(){
3186            if(!dlg){
3187                 dlg = new Roo.bootstrap.Modal( {
3188                     //draggable: true,
3189                     //resizable:false,
3190                     //constraintoviewport:false,
3191                     //fixedcenter:true,
3192                     //collapsible : false,
3193                     //shim:true,
3194                     //modal: true,
3195                 //    width: 'auto',
3196                   //  height:100,
3197                     //buttonAlign:"center",
3198                     closeClick : function(){
3199                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3200                             handleButton("no");
3201                         }else{
3202                             handleButton("cancel");
3203                         }
3204                     }
3205                 });
3206                 dlg.render();
3207                 dlg.on("hide", handleHide);
3208                 mask = dlg.mask;
3209                 //dlg.addKeyListener(27, handleEsc);
3210                 buttons = {};
3211                 this.buttons = buttons;
3212                 var bt = this.buttonText;
3213                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3214                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3215                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3216                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3217                 //Roo.log(buttons);
3218                 bodyEl = dlg.bodyEl.createChild({
3219
3220                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3221                         '<textarea class="roo-mb-textarea"></textarea>' +
3222                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3223                 });
3224                 msgEl = bodyEl.dom.firstChild;
3225                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3226                 textboxEl.enableDisplayMode();
3227                 textboxEl.addKeyListener([10,13], function(){
3228                     if(dlg.isVisible() && opt && opt.buttons){
3229                         if(opt.buttons.ok){
3230                             handleButton("ok");
3231                         }else if(opt.buttons.yes){
3232                             handleButton("yes");
3233                         }
3234                     }
3235                 });
3236                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3237                 textareaEl.enableDisplayMode();
3238                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3239                 progressEl.enableDisplayMode();
3240                 
3241                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3242                 var pf = progressEl.dom.firstChild;
3243                 if (pf) {
3244                     pp = Roo.get(pf.firstChild);
3245                     pp.setHeight(pf.offsetHeight);
3246                 }
3247                 
3248             }
3249             return dlg;
3250         },
3251
3252         /**
3253          * Updates the message box body text
3254          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3255          * the XHTML-compliant non-breaking space character '&amp;#160;')
3256          * @return {Roo.MessageBox} This message box
3257          */
3258         updateText : function(text)
3259         {
3260             if(!dlg.isVisible() && !opt.width){
3261                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3262                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3263             }
3264             msgEl.innerHTML = text || '&#160;';
3265       
3266             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3267             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3268             var w = Math.max(
3269                     Math.min(opt.width || cw , this.maxWidth), 
3270                     Math.max(opt.minWidth || this.minWidth, bwidth)
3271             );
3272             if(opt.prompt){
3273                 activeTextEl.setWidth(w);
3274             }
3275             if(dlg.isVisible()){
3276                 dlg.fixedcenter = false;
3277             }
3278             // to big, make it scroll. = But as usual stupid IE does not support
3279             // !important..
3280             
3281             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3282                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3283                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3284             } else {
3285                 bodyEl.dom.style.height = '';
3286                 bodyEl.dom.style.overflowY = '';
3287             }
3288             if (cw > w) {
3289                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3290             } else {
3291                 bodyEl.dom.style.overflowX = '';
3292             }
3293             
3294             dlg.setContentSize(w, bodyEl.getHeight());
3295             if(dlg.isVisible()){
3296                 dlg.fixedcenter = true;
3297             }
3298             return this;
3299         },
3300
3301         /**
3302          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3303          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3304          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3305          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3306          * @return {Roo.MessageBox} This message box
3307          */
3308         updateProgress : function(value, text){
3309             if(text){
3310                 this.updateText(text);
3311             }
3312             
3313             if (pp) { // weird bug on my firefox - for some reason this is not defined
3314                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3315                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3316             }
3317             return this;
3318         },        
3319
3320         /**
3321          * Returns true if the message box is currently displayed
3322          * @return {Boolean} True if the message box is visible, else false
3323          */
3324         isVisible : function(){
3325             return dlg && dlg.isVisible();  
3326         },
3327
3328         /**
3329          * Hides the message box if it is displayed
3330          */
3331         hide : function(){
3332             if(this.isVisible()){
3333                 dlg.hide();
3334             }  
3335         },
3336
3337         /**
3338          * Displays a new message box, or reinitializes an existing message box, based on the config options
3339          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3340          * The following config object properties are supported:
3341          * <pre>
3342 Property    Type             Description
3343 ----------  ---------------  ------------------------------------------------------------------------------------
3344 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3345                                    closes (defaults to undefined)
3346 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3347                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3348 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3349                                    progress and wait dialogs will ignore this property and always hide the
3350                                    close button as they can only be closed programmatically.
3351 cls               String           A custom CSS class to apply to the message box element
3352 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3353                                    displayed (defaults to 75)
3354 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3355                                    function will be btn (the name of the button that was clicked, if applicable,
3356                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3357                                    Progress and wait dialogs will ignore this option since they do not respond to
3358                                    user actions and can only be closed programmatically, so any required function
3359                                    should be called by the same code after it closes the dialog.
3360 icon              String           A CSS class that provides a background image to be used as an icon for
3361                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3362 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3363 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3364 modal             Boolean          False to allow user interaction with the page while the message box is
3365                                    displayed (defaults to true)
3366 msg               String           A string that will replace the existing message box body text (defaults
3367                                    to the XHTML-compliant non-breaking space character '&#160;')
3368 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3369 progress          Boolean          True to display a progress bar (defaults to false)
3370 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3371 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3372 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3373 title             String           The title text
3374 value             String           The string value to set into the active textbox element if displayed
3375 wait              Boolean          True to display a progress bar (defaults to false)
3376 width             Number           The width of the dialog in pixels
3377 </pre>
3378          *
3379          * Example usage:
3380          * <pre><code>
3381 Roo.Msg.show({
3382    title: 'Address',
3383    msg: 'Please enter your address:',
3384    width: 300,
3385    buttons: Roo.MessageBox.OKCANCEL,
3386    multiline: true,
3387    fn: saveAddress,
3388    animEl: 'addAddressBtn'
3389 });
3390 </code></pre>
3391          * @param {Object} config Configuration options
3392          * @return {Roo.MessageBox} This message box
3393          */
3394         show : function(options)
3395         {
3396             
3397             // this causes nightmares if you show one dialog after another
3398             // especially on callbacks..
3399              
3400             if(this.isVisible()){
3401                 
3402                 this.hide();
3403                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3404                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3405                 Roo.log("New Dialog Message:" +  options.msg )
3406                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3407                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3408                 
3409             }
3410             var d = this.getDialog();
3411             opt = options;
3412             d.setTitle(opt.title || "&#160;");
3413             d.closeEl.setDisplayed(opt.closable !== false);
3414             activeTextEl = textboxEl;
3415             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3416             if(opt.prompt){
3417                 if(opt.multiline){
3418                     textboxEl.hide();
3419                     textareaEl.show();
3420                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3421                         opt.multiline : this.defaultTextHeight);
3422                     activeTextEl = textareaEl;
3423                 }else{
3424                     textboxEl.show();
3425                     textareaEl.hide();
3426                 }
3427             }else{
3428                 textboxEl.hide();
3429                 textareaEl.hide();
3430             }
3431             progressEl.setDisplayed(opt.progress === true);
3432             this.updateProgress(0);
3433             activeTextEl.dom.value = opt.value || "";
3434             if(opt.prompt){
3435                 dlg.setDefaultButton(activeTextEl);
3436             }else{
3437                 var bs = opt.buttons;
3438                 var db = null;
3439                 if(bs && bs.ok){
3440                     db = buttons["ok"];
3441                 }else if(bs && bs.yes){
3442                     db = buttons["yes"];
3443                 }
3444                 dlg.setDefaultButton(db);
3445             }
3446             bwidth = updateButtons(opt.buttons);
3447             this.updateText(opt.msg);
3448             if(opt.cls){
3449                 d.el.addClass(opt.cls);
3450             }
3451             d.proxyDrag = opt.proxyDrag === true;
3452             d.modal = opt.modal !== false;
3453             d.mask = opt.modal !== false ? mask : false;
3454             if(!d.isVisible()){
3455                 // force it to the end of the z-index stack so it gets a cursor in FF
3456                 document.body.appendChild(dlg.el.dom);
3457                 d.animateTarget = null;
3458                 d.show(options.animEl);
3459             }
3460             return this;
3461         },
3462
3463         /**
3464          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3465          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3466          * and closing the message box when the process is complete.
3467          * @param {String} title The title bar text
3468          * @param {String} msg The message box body text
3469          * @return {Roo.MessageBox} This message box
3470          */
3471         progress : function(title, msg){
3472             this.show({
3473                 title : title,
3474                 msg : msg,
3475                 buttons: false,
3476                 progress:true,
3477                 closable:false,
3478                 minWidth: this.minProgressWidth,
3479                 modal : true
3480             });
3481             return this;
3482         },
3483
3484         /**
3485          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3486          * If a callback function is passed it will be called after the user clicks the button, and the
3487          * id of the button that was clicked will be passed as the only parameter to the callback
3488          * (could also be the top-right close button).
3489          * @param {String} title The title bar text
3490          * @param {String} msg The message box body text
3491          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3492          * @param {Object} scope (optional) The scope of the callback function
3493          * @return {Roo.MessageBox} This message box
3494          */
3495         alert : function(title, msg, fn, scope)
3496         {
3497             this.show({
3498                 title : title,
3499                 msg : msg,
3500                 buttons: this.OK,
3501                 fn: fn,
3502                 closable : false,
3503                 scope : scope,
3504                 modal : true
3505             });
3506             return this;
3507         },
3508
3509         /**
3510          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3511          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3512          * You are responsible for closing the message box when the process is complete.
3513          * @param {String} msg The message box body text
3514          * @param {String} title (optional) The title bar text
3515          * @return {Roo.MessageBox} This message box
3516          */
3517         wait : function(msg, title){
3518             this.show({
3519                 title : title,
3520                 msg : msg,
3521                 buttons: false,
3522                 closable:false,
3523                 progress:true,
3524                 modal:true,
3525                 width:300,
3526                 wait:true
3527             });
3528             waitTimer = Roo.TaskMgr.start({
3529                 run: function(i){
3530                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3531                 },
3532                 interval: 1000
3533             });
3534             return this;
3535         },
3536
3537         /**
3538          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3539          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3540          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3541          * @param {String} title The title bar text
3542          * @param {String} msg The message box body text
3543          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3544          * @param {Object} scope (optional) The scope of the callback function
3545          * @return {Roo.MessageBox} This message box
3546          */
3547         confirm : function(title, msg, fn, scope){
3548             this.show({
3549                 title : title,
3550                 msg : msg,
3551                 buttons: this.YESNO,
3552                 fn: fn,
3553                 scope : scope,
3554                 modal : true
3555             });
3556             return this;
3557         },
3558
3559         /**
3560          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3561          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3562          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3563          * (could also be the top-right close button) and the text that was entered will be passed as the two
3564          * parameters to the callback.
3565          * @param {String} title The title bar text
3566          * @param {String} msg The message box body text
3567          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3568          * @param {Object} scope (optional) The scope of the callback function
3569          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3570          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3571          * @return {Roo.MessageBox} This message box
3572          */
3573         prompt : function(title, msg, fn, scope, multiline){
3574             this.show({
3575                 title : title,
3576                 msg : msg,
3577                 buttons: this.OKCANCEL,
3578                 fn: fn,
3579                 minWidth:250,
3580                 scope : scope,
3581                 prompt:true,
3582                 multiline: multiline,
3583                 modal : true
3584             });
3585             return this;
3586         },
3587
3588         /**
3589          * Button config that displays a single OK button
3590          * @type Object
3591          */
3592         OK : {ok:true},
3593         /**
3594          * Button config that displays Yes and No buttons
3595          * @type Object
3596          */
3597         YESNO : {yes:true, no:true},
3598         /**
3599          * Button config that displays OK and Cancel buttons
3600          * @type Object
3601          */
3602         OKCANCEL : {ok:true, cancel:true},
3603         /**
3604          * Button config that displays Yes, No and Cancel buttons
3605          * @type Object
3606          */
3607         YESNOCANCEL : {yes:true, no:true, cancel:true},
3608
3609         /**
3610          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3611          * @type Number
3612          */
3613         defaultTextHeight : 75,
3614         /**
3615          * The maximum width in pixels of the message box (defaults to 600)
3616          * @type Number
3617          */
3618         maxWidth : 600,
3619         /**
3620          * The minimum width in pixels of the message box (defaults to 100)
3621          * @type Number
3622          */
3623         minWidth : 100,
3624         /**
3625          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3626          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3627          * @type Number
3628          */
3629         minProgressWidth : 250,
3630         /**
3631          * An object containing the default button text strings that can be overriden for localized language support.
3632          * Supported properties are: ok, cancel, yes and no.
3633          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3634          * @type Object
3635          */
3636         buttonText : {
3637             ok : "OK",
3638             cancel : "Cancel",
3639             yes : "Yes",
3640             no : "No"
3641         }
3642     };
3643 }();
3644
3645 /**
3646  * Shorthand for {@link Roo.MessageBox}
3647  */
3648 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3649 Roo.Msg = Roo.Msg || Roo.MessageBox;
3650 /*
3651  * - LGPL
3652  *
3653  * navbar
3654  * 
3655  */
3656
3657 /**
3658  * @class Roo.bootstrap.Navbar
3659  * @extends Roo.bootstrap.Component
3660  * Bootstrap Navbar class
3661
3662  * @constructor
3663  * Create a new Navbar
3664  * @param {Object} config The config object
3665  */
3666
3667
3668 Roo.bootstrap.Navbar = function(config){
3669     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3670     this.addEvents({
3671         // raw events
3672         /**
3673          * @event beforetoggle
3674          * Fire before toggle the menu
3675          * @param {Roo.EventObject} e
3676          */
3677         "beforetoggle" : true
3678     });
3679 };
3680
3681 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3682     
3683     
3684    
3685     // private
3686     navItems : false,
3687     loadMask : false,
3688     
3689     
3690     getAutoCreate : function(){
3691         
3692         
3693         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3694         
3695     },
3696     
3697     initEvents :function ()
3698     {
3699         //Roo.log(this.el.select('.navbar-toggle',true));
3700         this.el.select('.navbar-toggle',true).on('click', function() {
3701             if(this.fireEvent('beforetoggle', this) !== false){
3702                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3703             }
3704             
3705         }, this);
3706         
3707         var mark = {
3708             tag: "div",
3709             cls:"x-dlg-mask"
3710         };
3711         
3712         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3713         
3714         var size = this.el.getSize();
3715         this.maskEl.setSize(size.width, size.height);
3716         this.maskEl.enableDisplayMode("block");
3717         this.maskEl.hide();
3718         
3719         if(this.loadMask){
3720             this.maskEl.show();
3721         }
3722     },
3723     
3724     
3725     getChildContainer : function()
3726     {
3727         if (this.el.select('.collapse').getCount()) {
3728             return this.el.select('.collapse',true).first();
3729         }
3730         
3731         return this.el;
3732     },
3733     
3734     mask : function()
3735     {
3736         this.maskEl.show();
3737     },
3738     
3739     unmask : function()
3740     {
3741         this.maskEl.hide();
3742     } 
3743     
3744     
3745     
3746     
3747 });
3748
3749
3750
3751  
3752
3753  /*
3754  * - LGPL
3755  *
3756  * navbar
3757  * 
3758  */
3759
3760 /**
3761  * @class Roo.bootstrap.NavSimplebar
3762  * @extends Roo.bootstrap.Navbar
3763  * Bootstrap Sidebar class
3764  *
3765  * @cfg {Boolean} inverse is inverted color
3766  * 
3767  * @cfg {String} type (nav | pills | tabs)
3768  * @cfg {Boolean} arrangement stacked | justified
3769  * @cfg {String} align (left | right) alignment
3770  * 
3771  * @cfg {Boolean} main (true|false) main nav bar? default false
3772  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3773  * 
3774  * @cfg {String} tag (header|footer|nav|div) default is nav 
3775
3776  * 
3777  * 
3778  * 
3779  * @constructor
3780  * Create a new Sidebar
3781  * @param {Object} config The config object
3782  */
3783
3784
3785 Roo.bootstrap.NavSimplebar = function(config){
3786     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3787 };
3788
3789 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3790     
3791     inverse: false,
3792     
3793     type: false,
3794     arrangement: '',
3795     align : false,
3796     
3797     
3798     
3799     main : false,
3800     
3801     
3802     tag : false,
3803     
3804     
3805     getAutoCreate : function(){
3806         
3807         
3808         var cfg = {
3809             tag : this.tag || 'div',
3810             cls : 'navbar'
3811         };
3812           
3813         
3814         cfg.cn = [
3815             {
3816                 cls: 'nav',
3817                 tag : 'ul'
3818             }
3819         ];
3820         
3821          
3822         this.type = this.type || 'nav';
3823         if (['tabs','pills'].indexOf(this.type)!==-1) {
3824             cfg.cn[0].cls += ' nav-' + this.type
3825         
3826         
3827         } else {
3828             if (this.type!=='nav') {
3829                 Roo.log('nav type must be nav/tabs/pills')
3830             }
3831             cfg.cn[0].cls += ' navbar-nav'
3832         }
3833         
3834         
3835         
3836         
3837         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3838             cfg.cn[0].cls += ' nav-' + this.arrangement;
3839         }
3840         
3841         
3842         if (this.align === 'right') {
3843             cfg.cn[0].cls += ' navbar-right';
3844         }
3845         
3846         if (this.inverse) {
3847             cfg.cls += ' navbar-inverse';
3848             
3849         }
3850         
3851         
3852         return cfg;
3853     
3854         
3855     }
3856     
3857     
3858     
3859 });
3860
3861
3862
3863  
3864
3865  
3866        /*
3867  * - LGPL
3868  *
3869  * navbar
3870  * 
3871  */
3872
3873 /**
3874  * @class Roo.bootstrap.NavHeaderbar
3875  * @extends Roo.bootstrap.NavSimplebar
3876  * Bootstrap Sidebar class
3877  *
3878  * @cfg {String} brand what is brand
3879  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3880  * @cfg {String} brand_href href of the brand
3881  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3882  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3883  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3884  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3885  * 
3886  * @constructor
3887  * Create a new Sidebar
3888  * @param {Object} config The config object
3889  */
3890
3891
3892 Roo.bootstrap.NavHeaderbar = function(config){
3893     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3894       
3895 };
3896
3897 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3898     
3899     position: '',
3900     brand: '',
3901     brand_href: false,
3902     srButton : true,
3903     autohide : false,
3904     desktopCenter : false,
3905    
3906     
3907     getAutoCreate : function(){
3908         
3909         var   cfg = {
3910             tag: this.nav || 'nav',
3911             cls: 'navbar',
3912             role: 'navigation',
3913             cn: []
3914         };
3915         
3916         var cn = cfg.cn;
3917         if (this.desktopCenter) {
3918             cn.push({cls : 'container', cn : []});
3919             cn = cn[0].cn;
3920         }
3921         
3922         if(this.srButton){
3923             cn.push({
3924                 tag: 'div',
3925                 cls: 'navbar-header',
3926                 cn: [
3927                     {
3928                         tag: 'button',
3929                         type: 'button',
3930                         cls: 'navbar-toggle',
3931                         'data-toggle': 'collapse',
3932                         cn: [
3933                             {
3934                                 tag: 'span',
3935                                 cls: 'sr-only',
3936                                 html: 'Toggle navigation'
3937                             },
3938                             {
3939                                 tag: 'span',
3940                                 cls: 'icon-bar'
3941                             },
3942                             {
3943                                 tag: 'span',
3944                                 cls: 'icon-bar'
3945                             },
3946                             {
3947                                 tag: 'span',
3948                                 cls: 'icon-bar'
3949                             }
3950                         ]
3951                     }
3952                 ]
3953             });
3954         }
3955         
3956         cn.push({
3957             tag: 'div',
3958             cls: 'collapse navbar-collapse',
3959             cn : []
3960         });
3961         
3962         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3963         
3964         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3965             cfg.cls += ' navbar-' + this.position;
3966             
3967             // tag can override this..
3968             
3969             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3970         }
3971         
3972         if (this.brand !== '') {
3973             cn[0].cn.push({
3974                 tag: 'a',
3975                 href: this.brand_href ? this.brand_href : '#',
3976                 cls: 'navbar-brand',
3977                 cn: [
3978                 this.brand
3979                 ]
3980             });
3981         }
3982         
3983         if(this.main){
3984             cfg.cls += ' main-nav';
3985         }
3986         
3987         
3988         return cfg;
3989
3990         
3991     },
3992     getHeaderChildContainer : function()
3993     {
3994         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3995             return this.el.select('.navbar-header',true).first();
3996         }
3997         
3998         return this.getChildContainer();
3999     },
4000     
4001     
4002     initEvents : function()
4003     {
4004         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4005         
4006         if (this.autohide) {
4007             
4008             var prevScroll = 0;
4009             var ft = this.el;
4010             
4011             Roo.get(document).on('scroll',function(e) {
4012                 var ns = Roo.get(document).getScroll().top;
4013                 var os = prevScroll;
4014                 prevScroll = ns;
4015                 
4016                 if(ns > os){
4017                     ft.removeClass('slideDown');
4018                     ft.addClass('slideUp');
4019                     return;
4020                 }
4021                 ft.removeClass('slideUp');
4022                 ft.addClass('slideDown');
4023                  
4024               
4025           },this);
4026         }
4027     }    
4028     
4029 });
4030
4031
4032
4033  
4034
4035  /*
4036  * - LGPL
4037  *
4038  * navbar
4039  * 
4040  */
4041
4042 /**
4043  * @class Roo.bootstrap.NavSidebar
4044  * @extends Roo.bootstrap.Navbar
4045  * Bootstrap Sidebar class
4046  * 
4047  * @constructor
4048  * Create a new Sidebar
4049  * @param {Object} config The config object
4050  */
4051
4052
4053 Roo.bootstrap.NavSidebar = function(config){
4054     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4055 };
4056
4057 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4058     
4059     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4060     
4061     getAutoCreate : function(){
4062         
4063         
4064         return  {
4065             tag: 'div',
4066             cls: 'sidebar sidebar-nav'
4067         };
4068     
4069         
4070     }
4071     
4072     
4073     
4074 });
4075
4076
4077
4078  
4079
4080  /*
4081  * - LGPL
4082  *
4083  * nav group
4084  * 
4085  */
4086
4087 /**
4088  * @class Roo.bootstrap.NavGroup
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap NavGroup class
4091  * @cfg {String} align (left|right)
4092  * @cfg {Boolean} inverse
4093  * @cfg {String} type (nav|pills|tab) default nav
4094  * @cfg {String} navId - reference Id for navbar.
4095
4096  * 
4097  * @constructor
4098  * Create a new nav group
4099  * @param {Object} config The config object
4100  */
4101
4102 Roo.bootstrap.NavGroup = function(config){
4103     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4104     this.navItems = [];
4105    
4106     Roo.bootstrap.NavGroup.register(this);
4107      this.addEvents({
4108         /**
4109              * @event changed
4110              * Fires when the active item changes
4111              * @param {Roo.bootstrap.NavGroup} this
4112              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4113              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4114          */
4115         'changed': true
4116      });
4117     
4118 };
4119
4120 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4121     
4122     align: '',
4123     inverse: false,
4124     form: false,
4125     type: 'nav',
4126     navId : '',
4127     // private
4128     
4129     navItems : false, 
4130     
4131     getAutoCreate : function()
4132     {
4133         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4134         
4135         cfg = {
4136             tag : 'ul',
4137             cls: 'nav' 
4138         };
4139         
4140         if (['tabs','pills'].indexOf(this.type)!==-1) {
4141             cfg.cls += ' nav-' + this.type
4142         } else {
4143             if (this.type!=='nav') {
4144                 Roo.log('nav type must be nav/tabs/pills')
4145             }
4146             cfg.cls += ' navbar-nav'
4147         }
4148         
4149         if (this.parent() && this.parent().sidebar) {
4150             cfg = {
4151                 tag: 'ul',
4152                 cls: 'dashboard-menu sidebar-menu'
4153             };
4154             
4155             return cfg;
4156         }
4157         
4158         if (this.form === true) {
4159             cfg = {
4160                 tag: 'form',
4161                 cls: 'navbar-form'
4162             };
4163             
4164             if (this.align === 'right') {
4165                 cfg.cls += ' navbar-right';
4166             } else {
4167                 cfg.cls += ' navbar-left';
4168             }
4169         }
4170         
4171         if (this.align === 'right') {
4172             cfg.cls += ' navbar-right';
4173         }
4174         
4175         if (this.inverse) {
4176             cfg.cls += ' navbar-inverse';
4177             
4178         }
4179         
4180         
4181         return cfg;
4182     },
4183     /**
4184     * sets the active Navigation item
4185     * @param {Roo.bootstrap.NavItem} the new current navitem
4186     */
4187     setActiveItem : function(item)
4188     {
4189         var prev = false;
4190         Roo.each(this.navItems, function(v){
4191             if (v == item) {
4192                 return ;
4193             }
4194             if (v.isActive()) {
4195                 v.setActive(false, true);
4196                 prev = v;
4197                 
4198             }
4199             
4200         });
4201
4202         item.setActive(true, true);
4203         this.fireEvent('changed', this, item, prev);
4204         
4205         
4206     },
4207     /**
4208     * gets the active Navigation item
4209     * @return {Roo.bootstrap.NavItem} the current navitem
4210     */
4211     getActive : function()
4212     {
4213         
4214         var prev = false;
4215         Roo.each(this.navItems, function(v){
4216             
4217             if (v.isActive()) {
4218                 prev = v;
4219                 
4220             }
4221             
4222         });
4223         return prev;
4224     },
4225     
4226     indexOfNav : function()
4227     {
4228         
4229         var prev = false;
4230         Roo.each(this.navItems, function(v,i){
4231             
4232             if (v.isActive()) {
4233                 prev = i;
4234                 
4235             }
4236             
4237         });
4238         return prev;
4239     },
4240     /**
4241     * adds a Navigation item
4242     * @param {Roo.bootstrap.NavItem} the navitem to add
4243     */
4244     addItem : function(cfg)
4245     {
4246         var cn = new Roo.bootstrap.NavItem(cfg);
4247         this.register(cn);
4248         cn.parentId = this.id;
4249         cn.onRender(this.el, null);
4250         return cn;
4251     },
4252     /**
4253     * register a Navigation item
4254     * @param {Roo.bootstrap.NavItem} the navitem to add
4255     */
4256     register : function(item)
4257     {
4258         this.navItems.push( item);
4259         item.navId = this.navId;
4260     
4261     },
4262     
4263     /**
4264     * clear all the Navigation item
4265     */
4266    
4267     clearAll : function()
4268     {
4269         this.navItems = [];
4270         this.el.dom.innerHTML = '';
4271     },
4272     
4273     getNavItem: function(tabId)
4274     {
4275         var ret = false;
4276         Roo.each(this.navItems, function(e) {
4277             if (e.tabId == tabId) {
4278                ret =  e;
4279                return false;
4280             }
4281             return true;
4282             
4283         });
4284         return ret;
4285     },
4286     
4287     setActiveNext : function()
4288     {
4289         var i = this.indexOfNav(this.getActive());
4290         if (i > this.navItems.length) {
4291             return;
4292         }
4293         this.setActiveItem(this.navItems[i+1]);
4294     },
4295     setActivePrev : function()
4296     {
4297         var i = this.indexOfNav(this.getActive());
4298         if (i  < 1) {
4299             return;
4300         }
4301         this.setActiveItem(this.navItems[i-1]);
4302     },
4303     clearWasActive : function(except) {
4304         Roo.each(this.navItems, function(e) {
4305             if (e.tabId != except.tabId && e.was_active) {
4306                e.was_active = false;
4307                return false;
4308             }
4309             return true;
4310             
4311         });
4312     },
4313     getWasActive : function ()
4314     {
4315         var r = false;
4316         Roo.each(this.navItems, function(e) {
4317             if (e.was_active) {
4318                r = e;
4319                return false;
4320             }
4321             return true;
4322             
4323         });
4324         return r;
4325     }
4326     
4327     
4328 });
4329
4330  
4331 Roo.apply(Roo.bootstrap.NavGroup, {
4332     
4333     groups: {},
4334      /**
4335     * register a Navigation Group
4336     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4337     */
4338     register : function(navgrp)
4339     {
4340         this.groups[navgrp.navId] = navgrp;
4341         
4342     },
4343     /**
4344     * fetch a Navigation Group based on the navigation ID
4345     * @param {string} the navgroup to add
4346     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4347     */
4348     get: function(navId) {
4349         if (typeof(this.groups[navId]) == 'undefined') {
4350             return false;
4351             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4352         }
4353         return this.groups[navId] ;
4354     }
4355     
4356     
4357     
4358 });
4359
4360  /*
4361  * - LGPL
4362  *
4363  * row
4364  * 
4365  */
4366
4367 /**
4368  * @class Roo.bootstrap.NavItem
4369  * @extends Roo.bootstrap.Component
4370  * Bootstrap Navbar.NavItem class
4371  * @cfg {String} href  link to
4372  * @cfg {String} html content of button
4373  * @cfg {String} badge text inside badge
4374  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4375  * @cfg {String} glyphicon name of glyphicon
4376  * @cfg {String} icon name of font awesome icon
4377  * @cfg {Boolean} active Is item active
4378  * @cfg {Boolean} disabled Is item disabled
4379  
4380  * @cfg {Boolean} preventDefault (true | false) default false
4381  * @cfg {String} tabId the tab that this item activates.
4382  * @cfg {String} tagtype (a|span) render as a href or span?
4383  * @cfg {Boolean} animateRef (true|false) link to element default false  
4384   
4385  * @constructor
4386  * Create a new Navbar Item
4387  * @param {Object} config The config object
4388  */
4389 Roo.bootstrap.NavItem = function(config){
4390     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4391     this.addEvents({
4392         // raw events
4393         /**
4394          * @event click
4395          * The raw click event for the entire grid.
4396          * @param {Roo.EventObject} e
4397          */
4398         "click" : true,
4399          /**
4400             * @event changed
4401             * Fires when the active item active state changes
4402             * @param {Roo.bootstrap.NavItem} this
4403             * @param {boolean} state the new state
4404              
4405          */
4406         'changed': true,
4407         /**
4408             * @event scrollto
4409             * Fires when scroll to element
4410             * @param {Roo.bootstrap.NavItem} this
4411             * @param {Object} options
4412             * @param {Roo.EventObject} e
4413              
4414          */
4415         'scrollto': true
4416     });
4417    
4418 };
4419
4420 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4421     
4422     href: false,
4423     html: '',
4424     badge: '',
4425     icon: false,
4426     glyphicon: false,
4427     active: false,
4428     preventDefault : false,
4429     tabId : false,
4430     tagtype : 'a',
4431     disabled : false,
4432     animateRef : false,
4433     was_active : false,
4434     
4435     getAutoCreate : function(){
4436          
4437         var cfg = {
4438             tag: 'li',
4439             cls: 'nav-item'
4440             
4441         };
4442         
4443         if (this.active) {
4444             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4445         }
4446         if (this.disabled) {
4447             cfg.cls += ' disabled';
4448         }
4449         
4450         if (this.href || this.html || this.glyphicon || this.icon) {
4451             cfg.cn = [
4452                 {
4453                     tag: this.tagtype,
4454                     href : this.href || "#",
4455                     html: this.html || ''
4456                 }
4457             ];
4458             
4459             if (this.icon) {
4460                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4461             }
4462
4463             if(this.glyphicon) {
4464                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4465             }
4466             
4467             if (this.menu) {
4468                 
4469                 cfg.cn[0].html += " <span class='caret'></span>";
4470              
4471             }
4472             
4473             if (this.badge !== '') {
4474                  
4475                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4476             }
4477         }
4478         
4479         
4480         
4481         return cfg;
4482     },
4483     initEvents: function() 
4484     {
4485         if (typeof (this.menu) != 'undefined') {
4486             this.menu.parentType = this.xtype;
4487             this.menu.triggerEl = this.el;
4488             this.menu = this.addxtype(Roo.apply({}, this.menu));
4489         }
4490         
4491         this.el.select('a',true).on('click', this.onClick, this);
4492         
4493         if(this.tagtype == 'span'){
4494             this.el.select('span',true).on('click', this.onClick, this);
4495         }
4496        
4497         // at this point parent should be available..
4498         this.parent().register(this);
4499     },
4500     
4501     onClick : function(e)
4502     {
4503         if (e.getTarget('.dropdown-menu-item')) {
4504             // did you click on a menu itemm.... - then don't trigger onclick..
4505             return;
4506         }
4507         
4508         if(
4509                 this.preventDefault || 
4510                 this.href == '#' 
4511         ){
4512             Roo.log("NavItem - prevent Default?");
4513             e.preventDefault();
4514         }
4515         
4516         if (this.disabled) {
4517             return;
4518         }
4519         
4520         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4521         if (tg && tg.transition) {
4522             Roo.log("waiting for the transitionend");
4523             return;
4524         }
4525         
4526         
4527         
4528         //Roo.log("fire event clicked");
4529         if(this.fireEvent('click', this, e) === false){
4530             return;
4531         };
4532         
4533         if(this.tagtype == 'span'){
4534             return;
4535         }
4536         
4537         //Roo.log(this.href);
4538         var ael = this.el.select('a',true).first();
4539         //Roo.log(ael);
4540         
4541         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4542             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4543             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4544                 return; // ignore... - it's a 'hash' to another page.
4545             }
4546             Roo.log("NavItem - prevent Default?");
4547             e.preventDefault();
4548             this.scrollToElement(e);
4549         }
4550         
4551         
4552         var p =  this.parent();
4553    
4554         if (['tabs','pills'].indexOf(p.type)!==-1) {
4555             if (typeof(p.setActiveItem) !== 'undefined') {
4556                 p.setActiveItem(this);
4557             }
4558         }
4559         
4560         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4561         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4562             // remove the collapsed menu expand...
4563             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4564         }
4565     },
4566     
4567     isActive: function () {
4568         return this.active
4569     },
4570     setActive : function(state, fire, is_was_active)
4571     {
4572         if (this.active && !state && this.navId) {
4573             this.was_active = true;
4574             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4575             if (nv) {
4576                 nv.clearWasActive(this);
4577             }
4578             
4579         }
4580         this.active = state;
4581         
4582         if (!state ) {
4583             this.el.removeClass('active');
4584         } else if (!this.el.hasClass('active')) {
4585             this.el.addClass('active');
4586         }
4587         if (fire) {
4588             this.fireEvent('changed', this, state);
4589         }
4590         
4591         // show a panel if it's registered and related..
4592         
4593         if (!this.navId || !this.tabId || !state || is_was_active) {
4594             return;
4595         }
4596         
4597         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4598         if (!tg) {
4599             return;
4600         }
4601         var pan = tg.getPanelByName(this.tabId);
4602         if (!pan) {
4603             return;
4604         }
4605         // if we can not flip to new panel - go back to old nav highlight..
4606         if (false == tg.showPanel(pan)) {
4607             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4608             if (nv) {
4609                 var onav = nv.getWasActive();
4610                 if (onav) {
4611                     onav.setActive(true, false, true);
4612                 }
4613             }
4614             
4615         }
4616         
4617         
4618         
4619     },
4620      // this should not be here...
4621     setDisabled : function(state)
4622     {
4623         this.disabled = state;
4624         if (!state ) {
4625             this.el.removeClass('disabled');
4626         } else if (!this.el.hasClass('disabled')) {
4627             this.el.addClass('disabled');
4628         }
4629         
4630     },
4631     
4632     /**
4633      * Fetch the element to display the tooltip on.
4634      * @return {Roo.Element} defaults to this.el
4635      */
4636     tooltipEl : function()
4637     {
4638         return this.el.select('' + this.tagtype + '', true).first();
4639     },
4640     
4641     scrollToElement : function(e)
4642     {
4643         var c = document.body;
4644         
4645         /*
4646          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4647          */
4648         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4649             c = document.documentElement;
4650         }
4651         
4652         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4653         
4654         if(!target){
4655             return;
4656         }
4657
4658         var o = target.calcOffsetsTo(c);
4659         
4660         var options = {
4661             target : target,
4662             value : o[1]
4663         };
4664         
4665         this.fireEvent('scrollto', this, options, e);
4666         
4667         Roo.get(c).scrollTo('top', options.value, true);
4668         
4669         return;
4670     }
4671 });
4672  
4673
4674  /*
4675  * - LGPL
4676  *
4677  * sidebar item
4678  *
4679  *  li
4680  *    <span> icon </span>
4681  *    <span> text </span>
4682  *    <span>badge </span>
4683  */
4684
4685 /**
4686  * @class Roo.bootstrap.NavSidebarItem
4687  * @extends Roo.bootstrap.NavItem
4688  * Bootstrap Navbar.NavSidebarItem class
4689  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4690  * {Boolean} open is the menu open
4691  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4692  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4693  * {String} buttonSize (sm|md|lg)the extra classes for the button
4694  * {Boolean} showArrow show arrow next to the text (default true)
4695  * @constructor
4696  * Create a new Navbar Button
4697  * @param {Object} config The config object
4698  */
4699 Roo.bootstrap.NavSidebarItem = function(config){
4700     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4701     this.addEvents({
4702         // raw events
4703         /**
4704          * @event click
4705          * The raw click event for the entire grid.
4706          * @param {Roo.EventObject} e
4707          */
4708         "click" : true,
4709          /**
4710             * @event changed
4711             * Fires when the active item active state changes
4712             * @param {Roo.bootstrap.NavSidebarItem} this
4713             * @param {boolean} state the new state
4714              
4715          */
4716         'changed': true
4717     });
4718    
4719 };
4720
4721 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4722     
4723     badgeWeight : 'default',
4724     
4725     open: false,
4726     
4727     buttonView : false,
4728     
4729     buttonWeight : 'default',
4730     
4731     buttonSize : 'md',
4732     
4733     showArrow : true,
4734     
4735     getAutoCreate : function(){
4736         
4737         
4738         var a = {
4739                 tag: 'a',
4740                 href : this.href || '#',
4741                 cls: '',
4742                 html : '',
4743                 cn : []
4744         };
4745         
4746         if(this.buttonView){
4747             a = {
4748                 tag: 'button',
4749                 href : this.href || '#',
4750                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4751                 html : this.html,
4752                 cn : []
4753             };
4754         }
4755         
4756         var cfg = {
4757             tag: 'li',
4758             cls: '',
4759             cn: [ a ]
4760         };
4761         
4762         if (this.active) {
4763             cfg.cls += ' active';
4764         }
4765         
4766         if (this.disabled) {
4767             cfg.cls += ' disabled';
4768         }
4769         if (this.open) {
4770             cfg.cls += ' open x-open';
4771         }
4772         // left icon..
4773         if (this.glyphicon || this.icon) {
4774             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4775             a.cn.push({ tag : 'i', cls : c }) ;
4776         }
4777         
4778         if(!this.buttonView){
4779             var span = {
4780                 tag: 'span',
4781                 html : this.html || ''
4782             };
4783
4784             a.cn.push(span);
4785             
4786         }
4787         
4788         if (this.badge !== '') {
4789             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4790         }
4791         
4792         if (this.menu) {
4793             
4794             if(this.showArrow){
4795                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4796             }
4797             
4798             a.cls += ' dropdown-toggle treeview' ;
4799         }
4800         
4801         return cfg;
4802     },
4803     
4804     initEvents : function()
4805     { 
4806         if (typeof (this.menu) != 'undefined') {
4807             this.menu.parentType = this.xtype;
4808             this.menu.triggerEl = this.el;
4809             this.menu = this.addxtype(Roo.apply({}, this.menu));
4810         }
4811         
4812         this.el.on('click', this.onClick, this);
4813         
4814         if(this.badge !== ''){
4815             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4816         }
4817         
4818     },
4819     
4820     onClick : function(e)
4821     {
4822         if(this.disabled){
4823             e.preventDefault();
4824             return;
4825         }
4826         
4827         if(this.preventDefault){
4828             e.preventDefault();
4829         }
4830         
4831         this.fireEvent('click', this);
4832     },
4833     
4834     disable : function()
4835     {
4836         this.setDisabled(true);
4837     },
4838     
4839     enable : function()
4840     {
4841         this.setDisabled(false);
4842     },
4843     
4844     setDisabled : function(state)
4845     {
4846         if(this.disabled == state){
4847             return;
4848         }
4849         
4850         this.disabled = state;
4851         
4852         if (state) {
4853             this.el.addClass('disabled');
4854             return;
4855         }
4856         
4857         this.el.removeClass('disabled');
4858         
4859         return;
4860     },
4861     
4862     setActive : function(state)
4863     {
4864         if(this.active == state){
4865             return;
4866         }
4867         
4868         this.active = state;
4869         
4870         if (state) {
4871             this.el.addClass('active');
4872             return;
4873         }
4874         
4875         this.el.removeClass('active');
4876         
4877         return;
4878     },
4879     
4880     isActive: function () 
4881     {
4882         return this.active;
4883     },
4884     
4885     setBadge : function(str)
4886     {
4887         if(!this.badgeEl){
4888             return;
4889         }
4890         
4891         this.badgeEl.dom.innerHTML = str;
4892     }
4893     
4894    
4895      
4896  
4897 });
4898  
4899
4900  /*
4901  * - LGPL
4902  *
4903  * row
4904  * 
4905  */
4906
4907 /**
4908  * @class Roo.bootstrap.Row
4909  * @extends Roo.bootstrap.Component
4910  * Bootstrap Row class (contains columns...)
4911  * 
4912  * @constructor
4913  * Create a new Row
4914  * @param {Object} config The config object
4915  */
4916
4917 Roo.bootstrap.Row = function(config){
4918     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4919 };
4920
4921 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4922     
4923     getAutoCreate : function(){
4924        return {
4925             cls: 'row clearfix'
4926        };
4927     }
4928     
4929     
4930 });
4931
4932  
4933
4934  /*
4935  * - LGPL
4936  *
4937  * element
4938  * 
4939  */
4940
4941 /**
4942  * @class Roo.bootstrap.Element
4943  * @extends Roo.bootstrap.Component
4944  * Bootstrap Element class
4945  * @cfg {String} html contents of the element
4946  * @cfg {String} tag tag of the element
4947  * @cfg {String} cls class of the element
4948  * @cfg {Boolean} preventDefault (true|false) default false
4949  * @cfg {Boolean} clickable (true|false) default false
4950  * 
4951  * @constructor
4952  * Create a new Element
4953  * @param {Object} config The config object
4954  */
4955
4956 Roo.bootstrap.Element = function(config){
4957     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4958     
4959     this.addEvents({
4960         // raw events
4961         /**
4962          * @event click
4963          * When a element is chick
4964          * @param {Roo.bootstrap.Element} this
4965          * @param {Roo.EventObject} e
4966          */
4967         "click" : true
4968     });
4969 };
4970
4971 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4972     
4973     tag: 'div',
4974     cls: '',
4975     html: '',
4976     preventDefault: false, 
4977     clickable: false,
4978     
4979     getAutoCreate : function(){
4980         
4981         var cfg = {
4982             tag: this.tag,
4983             // cls: this.cls, double assign in parent class Component.js :: onRender
4984             html: this.html
4985         };
4986         
4987         return cfg;
4988     },
4989     
4990     initEvents: function() 
4991     {
4992         Roo.bootstrap.Element.superclass.initEvents.call(this);
4993         
4994         if(this.clickable){
4995             this.el.on('click', this.onClick, this);
4996         }
4997         
4998     },
4999     
5000     onClick : function(e)
5001     {
5002         if(this.preventDefault){
5003             e.preventDefault();
5004         }
5005         
5006         this.fireEvent('click', this, e);
5007     },
5008     
5009     getValue : function()
5010     {
5011         return this.el.dom.innerHTML;
5012     },
5013     
5014     setValue : function(value)
5015     {
5016         this.el.dom.innerHTML = value;
5017     }
5018    
5019 });
5020
5021  
5022
5023  /*
5024  * - LGPL
5025  *
5026  * pagination
5027  * 
5028  */
5029
5030 /**
5031  * @class Roo.bootstrap.Pagination
5032  * @extends Roo.bootstrap.Component
5033  * Bootstrap Pagination class
5034  * @cfg {String} size xs | sm | md | lg
5035  * @cfg {Boolean} inverse false | true
5036  * 
5037  * @constructor
5038  * Create a new Pagination
5039  * @param {Object} config The config object
5040  */
5041
5042 Roo.bootstrap.Pagination = function(config){
5043     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5044 };
5045
5046 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5047     
5048     cls: false,
5049     size: false,
5050     inverse: false,
5051     
5052     getAutoCreate : function(){
5053         var cfg = {
5054             tag: 'ul',
5055                 cls: 'pagination'
5056         };
5057         if (this.inverse) {
5058             cfg.cls += ' inverse';
5059         }
5060         if (this.html) {
5061             cfg.html=this.html;
5062         }
5063         if (this.cls) {
5064             cfg.cls += " " + this.cls;
5065         }
5066         return cfg;
5067     }
5068    
5069 });
5070
5071  
5072
5073  /*
5074  * - LGPL
5075  *
5076  * Pagination item
5077  * 
5078  */
5079
5080
5081 /**
5082  * @class Roo.bootstrap.PaginationItem
5083  * @extends Roo.bootstrap.Component
5084  * Bootstrap PaginationItem class
5085  * @cfg {String} html text
5086  * @cfg {String} href the link
5087  * @cfg {Boolean} preventDefault (true | false) default true
5088  * @cfg {Boolean} active (true | false) default false
5089  * @cfg {Boolean} disabled default false
5090  * 
5091  * 
5092  * @constructor
5093  * Create a new PaginationItem
5094  * @param {Object} config The config object
5095  */
5096
5097
5098 Roo.bootstrap.PaginationItem = function(config){
5099     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5100     this.addEvents({
5101         // raw events
5102         /**
5103          * @event click
5104          * The raw click event for the entire grid.
5105          * @param {Roo.EventObject} e
5106          */
5107         "click" : true
5108     });
5109 };
5110
5111 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5112     
5113     href : false,
5114     html : false,
5115     preventDefault: true,
5116     active : false,
5117     cls : false,
5118     disabled: false,
5119     
5120     getAutoCreate : function(){
5121         var cfg= {
5122             tag: 'li',
5123             cn: [
5124                 {
5125                     tag : 'a',
5126                     href : this.href ? this.href : '#',
5127                     html : this.html ? this.html : ''
5128                 }
5129             ]
5130         };
5131         
5132         if(this.cls){
5133             cfg.cls = this.cls;
5134         }
5135         
5136         if(this.disabled){
5137             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5138         }
5139         
5140         if(this.active){
5141             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5142         }
5143         
5144         return cfg;
5145     },
5146     
5147     initEvents: function() {
5148         
5149         this.el.on('click', this.onClick, this);
5150         
5151     },
5152     onClick : function(e)
5153     {
5154         Roo.log('PaginationItem on click ');
5155         if(this.preventDefault){
5156             e.preventDefault();
5157         }
5158         
5159         if(this.disabled){
5160             return;
5161         }
5162         
5163         this.fireEvent('click', this, e);
5164     }
5165    
5166 });
5167
5168  
5169
5170  /*
5171  * - LGPL
5172  *
5173  * slider
5174  * 
5175  */
5176
5177
5178 /**
5179  * @class Roo.bootstrap.Slider
5180  * @extends Roo.bootstrap.Component
5181  * Bootstrap Slider class
5182  *    
5183  * @constructor
5184  * Create a new Slider
5185  * @param {Object} config The config object
5186  */
5187
5188 Roo.bootstrap.Slider = function(config){
5189     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5190 };
5191
5192 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5193     
5194     getAutoCreate : function(){
5195         
5196         var cfg = {
5197             tag: 'div',
5198             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5199             cn: [
5200                 {
5201                     tag: 'a',
5202                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5203                 }
5204             ]
5205         };
5206         
5207         return cfg;
5208     }
5209    
5210 });
5211
5212  /*
5213  * Based on:
5214  * Ext JS Library 1.1.1
5215  * Copyright(c) 2006-2007, Ext JS, LLC.
5216  *
5217  * Originally Released Under LGPL - original licence link has changed is not relivant.
5218  *
5219  * Fork - LGPL
5220  * <script type="text/javascript">
5221  */
5222  
5223
5224 /**
5225  * @class Roo.grid.ColumnModel
5226  * @extends Roo.util.Observable
5227  * This is the default implementation of a ColumnModel used by the Grid. It defines
5228  * the columns in the grid.
5229  * <br>Usage:<br>
5230  <pre><code>
5231  var colModel = new Roo.grid.ColumnModel([
5232         {header: "Ticker", width: 60, sortable: true, locked: true},
5233         {header: "Company Name", width: 150, sortable: true},
5234         {header: "Market Cap.", width: 100, sortable: true},
5235         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5236         {header: "Employees", width: 100, sortable: true, resizable: false}
5237  ]);
5238  </code></pre>
5239  * <p>
5240  
5241  * The config options listed for this class are options which may appear in each
5242  * individual column definition.
5243  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5244  * @constructor
5245  * @param {Object} config An Array of column config objects. See this class's
5246  * config objects for details.
5247 */
5248 Roo.grid.ColumnModel = function(config){
5249         /**
5250      * The config passed into the constructor
5251      */
5252     this.config = config;
5253     this.lookup = {};
5254
5255     // if no id, create one
5256     // if the column does not have a dataIndex mapping,
5257     // map it to the order it is in the config
5258     for(var i = 0, len = config.length; i < len; i++){
5259         var c = config[i];
5260         if(typeof c.dataIndex == "undefined"){
5261             c.dataIndex = i;
5262         }
5263         if(typeof c.renderer == "string"){
5264             c.renderer = Roo.util.Format[c.renderer];
5265         }
5266         if(typeof c.id == "undefined"){
5267             c.id = Roo.id();
5268         }
5269         if(c.editor && c.editor.xtype){
5270             c.editor  = Roo.factory(c.editor, Roo.grid);
5271         }
5272         if(c.editor && c.editor.isFormField){
5273             c.editor = new Roo.grid.GridEditor(c.editor);
5274         }
5275         this.lookup[c.id] = c;
5276     }
5277
5278     /**
5279      * The width of columns which have no width specified (defaults to 100)
5280      * @type Number
5281      */
5282     this.defaultWidth = 100;
5283
5284     /**
5285      * Default sortable of columns which have no sortable specified (defaults to false)
5286      * @type Boolean
5287      */
5288     this.defaultSortable = false;
5289
5290     this.addEvents({
5291         /**
5292              * @event widthchange
5293              * Fires when the width of a column changes.
5294              * @param {ColumnModel} this
5295              * @param {Number} columnIndex The column index
5296              * @param {Number} newWidth The new width
5297              */
5298             "widthchange": true,
5299         /**
5300              * @event headerchange
5301              * Fires when the text of a header changes.
5302              * @param {ColumnModel} this
5303              * @param {Number} columnIndex The column index
5304              * @param {Number} newText The new header text
5305              */
5306             "headerchange": true,
5307         /**
5308              * @event hiddenchange
5309              * Fires when a column is hidden or "unhidden".
5310              * @param {ColumnModel} this
5311              * @param {Number} columnIndex The column index
5312              * @param {Boolean} hidden true if hidden, false otherwise
5313              */
5314             "hiddenchange": true,
5315             /**
5316          * @event columnmoved
5317          * Fires when a column is moved.
5318          * @param {ColumnModel} this
5319          * @param {Number} oldIndex
5320          * @param {Number} newIndex
5321          */
5322         "columnmoved" : true,
5323         /**
5324          * @event columlockchange
5325          * Fires when a column's locked state is changed
5326          * @param {ColumnModel} this
5327          * @param {Number} colIndex
5328          * @param {Boolean} locked true if locked
5329          */
5330         "columnlockchange" : true
5331     });
5332     Roo.grid.ColumnModel.superclass.constructor.call(this);
5333 };
5334 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5335     /**
5336      * @cfg {String} header The header text to display in the Grid view.
5337      */
5338     /**
5339      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5340      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5341      * specified, the column's index is used as an index into the Record's data Array.
5342      */
5343     /**
5344      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5345      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5346      */
5347     /**
5348      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5349      * Defaults to the value of the {@link #defaultSortable} property.
5350      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5351      */
5352     /**
5353      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5354      */
5355     /**
5356      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5357      */
5358     /**
5359      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5360      */
5361     /**
5362      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5363      */
5364     /**
5365      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5366      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5367      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5368      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5369      */
5370        /**
5371      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5372      */
5373     /**
5374      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5375      */
5376     /**
5377      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5378      */
5379     /**
5380      * @cfg {String} cursor (Optional)
5381      */
5382     /**
5383      * @cfg {String} tooltip (Optional)
5384      */
5385     /**
5386      * @cfg {Number} xs (Optional)
5387      */
5388     /**
5389      * @cfg {Number} sm (Optional)
5390      */
5391     /**
5392      * @cfg {Number} md (Optional)
5393      */
5394     /**
5395      * @cfg {Number} lg (Optional)
5396      */
5397     /**
5398      * Returns the id of the column at the specified index.
5399      * @param {Number} index The column index
5400      * @return {String} the id
5401      */
5402     getColumnId : function(index){
5403         return this.config[index].id;
5404     },
5405
5406     /**
5407      * Returns the column for a specified id.
5408      * @param {String} id The column id
5409      * @return {Object} the column
5410      */
5411     getColumnById : function(id){
5412         return this.lookup[id];
5413     },
5414
5415     
5416     /**
5417      * Returns the column for a specified dataIndex.
5418      * @param {String} dataIndex The column dataIndex
5419      * @return {Object|Boolean} the column or false if not found
5420      */
5421     getColumnByDataIndex: function(dataIndex){
5422         var index = this.findColumnIndex(dataIndex);
5423         return index > -1 ? this.config[index] : false;
5424     },
5425     
5426     /**
5427      * Returns the index for a specified column id.
5428      * @param {String} id The column id
5429      * @return {Number} the index, or -1 if not found
5430      */
5431     getIndexById : function(id){
5432         for(var i = 0, len = this.config.length; i < len; i++){
5433             if(this.config[i].id == id){
5434                 return i;
5435             }
5436         }
5437         return -1;
5438     },
5439     
5440     /**
5441      * Returns the index for a specified column dataIndex.
5442      * @param {String} dataIndex The column dataIndex
5443      * @return {Number} the index, or -1 if not found
5444      */
5445     
5446     findColumnIndex : function(dataIndex){
5447         for(var i = 0, len = this.config.length; i < len; i++){
5448             if(this.config[i].dataIndex == dataIndex){
5449                 return i;
5450             }
5451         }
5452         return -1;
5453     },
5454     
5455     
5456     moveColumn : function(oldIndex, newIndex){
5457         var c = this.config[oldIndex];
5458         this.config.splice(oldIndex, 1);
5459         this.config.splice(newIndex, 0, c);
5460         this.dataMap = null;
5461         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5462     },
5463
5464     isLocked : function(colIndex){
5465         return this.config[colIndex].locked === true;
5466     },
5467
5468     setLocked : function(colIndex, value, suppressEvent){
5469         if(this.isLocked(colIndex) == value){
5470             return;
5471         }
5472         this.config[colIndex].locked = value;
5473         if(!suppressEvent){
5474             this.fireEvent("columnlockchange", this, colIndex, value);
5475         }
5476     },
5477
5478     getTotalLockedWidth : function(){
5479         var totalWidth = 0;
5480         for(var i = 0; i < this.config.length; i++){
5481             if(this.isLocked(i) && !this.isHidden(i)){
5482                 this.totalWidth += this.getColumnWidth(i);
5483             }
5484         }
5485         return totalWidth;
5486     },
5487
5488     getLockedCount : function(){
5489         for(var i = 0, len = this.config.length; i < len; i++){
5490             if(!this.isLocked(i)){
5491                 return i;
5492             }
5493         }
5494         
5495         return this.config.length;
5496     },
5497
5498     /**
5499      * Returns the number of columns.
5500      * @return {Number}
5501      */
5502     getColumnCount : function(visibleOnly){
5503         if(visibleOnly === true){
5504             var c = 0;
5505             for(var i = 0, len = this.config.length; i < len; i++){
5506                 if(!this.isHidden(i)){
5507                     c++;
5508                 }
5509             }
5510             return c;
5511         }
5512         return this.config.length;
5513     },
5514
5515     /**
5516      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5517      * @param {Function} fn
5518      * @param {Object} scope (optional)
5519      * @return {Array} result
5520      */
5521     getColumnsBy : function(fn, scope){
5522         var r = [];
5523         for(var i = 0, len = this.config.length; i < len; i++){
5524             var c = this.config[i];
5525             if(fn.call(scope||this, c, i) === true){
5526                 r[r.length] = c;
5527             }
5528         }
5529         return r;
5530     },
5531
5532     /**
5533      * Returns true if the specified column is sortable.
5534      * @param {Number} col The column index
5535      * @return {Boolean}
5536      */
5537     isSortable : function(col){
5538         if(typeof this.config[col].sortable == "undefined"){
5539             return this.defaultSortable;
5540         }
5541         return this.config[col].sortable;
5542     },
5543
5544     /**
5545      * Returns the rendering (formatting) function defined for the column.
5546      * @param {Number} col The column index.
5547      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5548      */
5549     getRenderer : function(col){
5550         if(!this.config[col].renderer){
5551             return Roo.grid.ColumnModel.defaultRenderer;
5552         }
5553         return this.config[col].renderer;
5554     },
5555
5556     /**
5557      * Sets the rendering (formatting) function for a column.
5558      * @param {Number} col The column index
5559      * @param {Function} fn The function to use to process the cell's raw data
5560      * to return HTML markup for the grid view. The render function is called with
5561      * the following parameters:<ul>
5562      * <li>Data value.</li>
5563      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5564      * <li>css A CSS style string to apply to the table cell.</li>
5565      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5566      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5567      * <li>Row index</li>
5568      * <li>Column index</li>
5569      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5570      */
5571     setRenderer : function(col, fn){
5572         this.config[col].renderer = fn;
5573     },
5574
5575     /**
5576      * Returns the width for the specified column.
5577      * @param {Number} col The column index
5578      * @return {Number}
5579      */
5580     getColumnWidth : function(col){
5581         return this.config[col].width * 1 || this.defaultWidth;
5582     },
5583
5584     /**
5585      * Sets the width for a column.
5586      * @param {Number} col The column index
5587      * @param {Number} width The new width
5588      */
5589     setColumnWidth : function(col, width, suppressEvent){
5590         this.config[col].width = width;
5591         this.totalWidth = null;
5592         if(!suppressEvent){
5593              this.fireEvent("widthchange", this, col, width);
5594         }
5595     },
5596
5597     /**
5598      * Returns the total width of all columns.
5599      * @param {Boolean} includeHidden True to include hidden column widths
5600      * @return {Number}
5601      */
5602     getTotalWidth : function(includeHidden){
5603         if(!this.totalWidth){
5604             this.totalWidth = 0;
5605             for(var i = 0, len = this.config.length; i < len; i++){
5606                 if(includeHidden || !this.isHidden(i)){
5607                     this.totalWidth += this.getColumnWidth(i);
5608                 }
5609             }
5610         }
5611         return this.totalWidth;
5612     },
5613
5614     /**
5615      * Returns the header for the specified column.
5616      * @param {Number} col The column index
5617      * @return {String}
5618      */
5619     getColumnHeader : function(col){
5620         return this.config[col].header;
5621     },
5622
5623     /**
5624      * Sets the header for a column.
5625      * @param {Number} col The column index
5626      * @param {String} header The new header
5627      */
5628     setColumnHeader : function(col, header){
5629         this.config[col].header = header;
5630         this.fireEvent("headerchange", this, col, header);
5631     },
5632
5633     /**
5634      * Returns the tooltip for the specified column.
5635      * @param {Number} col The column index
5636      * @return {String}
5637      */
5638     getColumnTooltip : function(col){
5639             return this.config[col].tooltip;
5640     },
5641     /**
5642      * Sets the tooltip for a column.
5643      * @param {Number} col The column index
5644      * @param {String} tooltip The new tooltip
5645      */
5646     setColumnTooltip : function(col, tooltip){
5647             this.config[col].tooltip = tooltip;
5648     },
5649
5650     /**
5651      * Returns the dataIndex for the specified column.
5652      * @param {Number} col The column index
5653      * @return {Number}
5654      */
5655     getDataIndex : function(col){
5656         return this.config[col].dataIndex;
5657     },
5658
5659     /**
5660      * Sets the dataIndex for a column.
5661      * @param {Number} col The column index
5662      * @param {Number} dataIndex The new dataIndex
5663      */
5664     setDataIndex : function(col, dataIndex){
5665         this.config[col].dataIndex = dataIndex;
5666     },
5667
5668     
5669     
5670     /**
5671      * Returns true if the cell is editable.
5672      * @param {Number} colIndex The column index
5673      * @param {Number} rowIndex The row index - this is nto actually used..?
5674      * @return {Boolean}
5675      */
5676     isCellEditable : function(colIndex, rowIndex){
5677         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5678     },
5679
5680     /**
5681      * Returns the editor defined for the cell/column.
5682      * return false or null to disable editing.
5683      * @param {Number} colIndex The column index
5684      * @param {Number} rowIndex The row index
5685      * @return {Object}
5686      */
5687     getCellEditor : function(colIndex, rowIndex){
5688         return this.config[colIndex].editor;
5689     },
5690
5691     /**
5692      * Sets if a column is editable.
5693      * @param {Number} col The column index
5694      * @param {Boolean} editable True if the column is editable
5695      */
5696     setEditable : function(col, editable){
5697         this.config[col].editable = editable;
5698     },
5699
5700
5701     /**
5702      * Returns true if the column is hidden.
5703      * @param {Number} colIndex The column index
5704      * @return {Boolean}
5705      */
5706     isHidden : function(colIndex){
5707         return this.config[colIndex].hidden;
5708     },
5709
5710
5711     /**
5712      * Returns true if the column width cannot be changed
5713      */
5714     isFixed : function(colIndex){
5715         return this.config[colIndex].fixed;
5716     },
5717
5718     /**
5719      * Returns true if the column can be resized
5720      * @return {Boolean}
5721      */
5722     isResizable : function(colIndex){
5723         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5724     },
5725     /**
5726      * Sets if a column is hidden.
5727      * @param {Number} colIndex The column index
5728      * @param {Boolean} hidden True if the column is hidden
5729      */
5730     setHidden : function(colIndex, hidden){
5731         this.config[colIndex].hidden = hidden;
5732         this.totalWidth = null;
5733         this.fireEvent("hiddenchange", this, colIndex, hidden);
5734     },
5735
5736     /**
5737      * Sets the editor for a column.
5738      * @param {Number} col The column index
5739      * @param {Object} editor The editor object
5740      */
5741     setEditor : function(col, editor){
5742         this.config[col].editor = editor;
5743     }
5744 });
5745
5746 Roo.grid.ColumnModel.defaultRenderer = function(value)
5747 {
5748     if(typeof value == "object") {
5749         return value;
5750     }
5751         if(typeof value == "string" && value.length < 1){
5752             return "&#160;";
5753         }
5754     
5755         return String.format("{0}", value);
5756 };
5757
5758 // Alias for backwards compatibility
5759 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5760 /*
5761  * Based on:
5762  * Ext JS Library 1.1.1
5763  * Copyright(c) 2006-2007, Ext JS, LLC.
5764  *
5765  * Originally Released Under LGPL - original licence link has changed is not relivant.
5766  *
5767  * Fork - LGPL
5768  * <script type="text/javascript">
5769  */
5770  
5771 /**
5772  * @class Roo.LoadMask
5773  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5774  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5775  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5776  * element's UpdateManager load indicator and will be destroyed after the initial load.
5777  * @constructor
5778  * Create a new LoadMask
5779  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5780  * @param {Object} config The config object
5781  */
5782 Roo.LoadMask = function(el, config){
5783     this.el = Roo.get(el);
5784     Roo.apply(this, config);
5785     if(this.store){
5786         this.store.on('beforeload', this.onBeforeLoad, this);
5787         this.store.on('load', this.onLoad, this);
5788         this.store.on('loadexception', this.onLoadException, this);
5789         this.removeMask = false;
5790     }else{
5791         var um = this.el.getUpdateManager();
5792         um.showLoadIndicator = false; // disable the default indicator
5793         um.on('beforeupdate', this.onBeforeLoad, this);
5794         um.on('update', this.onLoad, this);
5795         um.on('failure', this.onLoad, this);
5796         this.removeMask = true;
5797     }
5798 };
5799
5800 Roo.LoadMask.prototype = {
5801     /**
5802      * @cfg {Boolean} removeMask
5803      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5804      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5805      */
5806     /**
5807      * @cfg {String} msg
5808      * The text to display in a centered loading message box (defaults to 'Loading...')
5809      */
5810     msg : 'Loading...',
5811     /**
5812      * @cfg {String} msgCls
5813      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5814      */
5815     msgCls : 'x-mask-loading',
5816
5817     /**
5818      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5819      * @type Boolean
5820      */
5821     disabled: false,
5822
5823     /**
5824      * Disables the mask to prevent it from being displayed
5825      */
5826     disable : function(){
5827        this.disabled = true;
5828     },
5829
5830     /**
5831      * Enables the mask so that it can be displayed
5832      */
5833     enable : function(){
5834         this.disabled = false;
5835     },
5836     
5837     onLoadException : function()
5838     {
5839         Roo.log(arguments);
5840         
5841         if (typeof(arguments[3]) != 'undefined') {
5842             Roo.MessageBox.alert("Error loading",arguments[3]);
5843         } 
5844         /*
5845         try {
5846             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5847                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5848             }   
5849         } catch(e) {
5850             
5851         }
5852         */
5853     
5854         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5855     },
5856     // private
5857     onLoad : function()
5858     {
5859         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5860     },
5861
5862     // private
5863     onBeforeLoad : function(){
5864         if(!this.disabled){
5865             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5866         }
5867     },
5868
5869     // private
5870     destroy : function(){
5871         if(this.store){
5872             this.store.un('beforeload', this.onBeforeLoad, this);
5873             this.store.un('load', this.onLoad, this);
5874             this.store.un('loadexception', this.onLoadException, this);
5875         }else{
5876             var um = this.el.getUpdateManager();
5877             um.un('beforeupdate', this.onBeforeLoad, this);
5878             um.un('update', this.onLoad, this);
5879             um.un('failure', this.onLoad, this);
5880         }
5881     }
5882 };/*
5883  * - LGPL
5884  *
5885  * table
5886  * 
5887  */
5888
5889 /**
5890  * @class Roo.bootstrap.Table
5891  * @extends Roo.bootstrap.Component
5892  * Bootstrap Table class
5893  * @cfg {String} cls table class
5894  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5895  * @cfg {String} bgcolor Specifies the background color for a table
5896  * @cfg {Number} border Specifies whether the table cells should have borders or not
5897  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5898  * @cfg {Number} cellspacing Specifies the space between cells
5899  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5900  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5901  * @cfg {String} sortable Specifies that the table should be sortable
5902  * @cfg {String} summary Specifies a summary of the content of a table
5903  * @cfg {Number} width Specifies the width of a table
5904  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5905  * 
5906  * @cfg {boolean} striped Should the rows be alternative striped
5907  * @cfg {boolean} bordered Add borders to the table
5908  * @cfg {boolean} hover Add hover highlighting
5909  * @cfg {boolean} condensed Format condensed
5910  * @cfg {boolean} responsive Format condensed
5911  * @cfg {Boolean} loadMask (true|false) default false
5912  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5913  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5914  * @cfg {Boolean} rowSelection (true|false) default false
5915  * @cfg {Boolean} cellSelection (true|false) default false
5916  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5917  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5918  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5919  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5920  
5921  * 
5922  * @constructor
5923  * Create a new Table
5924  * @param {Object} config The config object
5925  */
5926
5927 Roo.bootstrap.Table = function(config){
5928     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5929     
5930   
5931     
5932     // BC...
5933     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5934     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5935     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5936     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5937     
5938     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5939     if (this.sm) {
5940         this.sm.grid = this;
5941         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5942         this.sm = this.selModel;
5943         this.sm.xmodule = this.xmodule || false;
5944     }
5945     
5946     if (this.cm && typeof(this.cm.config) == 'undefined') {
5947         this.colModel = new Roo.grid.ColumnModel(this.cm);
5948         this.cm = this.colModel;
5949         this.cm.xmodule = this.xmodule || false;
5950     }
5951     if (this.store) {
5952         this.store= Roo.factory(this.store, Roo.data);
5953         this.ds = this.store;
5954         this.ds.xmodule = this.xmodule || false;
5955          
5956     }
5957     if (this.footer && this.store) {
5958         this.footer.dataSource = this.ds;
5959         this.footer = Roo.factory(this.footer);
5960     }
5961     
5962     /** @private */
5963     this.addEvents({
5964         /**
5965          * @event cellclick
5966          * Fires when a cell is clicked
5967          * @param {Roo.bootstrap.Table} this
5968          * @param {Roo.Element} el
5969          * @param {Number} rowIndex
5970          * @param {Number} columnIndex
5971          * @param {Roo.EventObject} e
5972          */
5973         "cellclick" : true,
5974         /**
5975          * @event celldblclick
5976          * Fires when a cell is double clicked
5977          * @param {Roo.bootstrap.Table} this
5978          * @param {Roo.Element} el
5979          * @param {Number} rowIndex
5980          * @param {Number} columnIndex
5981          * @param {Roo.EventObject} e
5982          */
5983         "celldblclick" : true,
5984         /**
5985          * @event rowclick
5986          * Fires when a row is clicked
5987          * @param {Roo.bootstrap.Table} this
5988          * @param {Roo.Element} el
5989          * @param {Number} rowIndex
5990          * @param {Roo.EventObject} e
5991          */
5992         "rowclick" : true,
5993         /**
5994          * @event rowdblclick
5995          * Fires when a row is double clicked
5996          * @param {Roo.bootstrap.Table} this
5997          * @param {Roo.Element} el
5998          * @param {Number} rowIndex
5999          * @param {Roo.EventObject} e
6000          */
6001         "rowdblclick" : true,
6002         /**
6003          * @event mouseover
6004          * Fires when a mouseover occur
6005          * @param {Roo.bootstrap.Table} this
6006          * @param {Roo.Element} el
6007          * @param {Number} rowIndex
6008          * @param {Number} columnIndex
6009          * @param {Roo.EventObject} e
6010          */
6011         "mouseover" : true,
6012         /**
6013          * @event mouseout
6014          * Fires when a mouseout occur
6015          * @param {Roo.bootstrap.Table} this
6016          * @param {Roo.Element} el
6017          * @param {Number} rowIndex
6018          * @param {Number} columnIndex
6019          * @param {Roo.EventObject} e
6020          */
6021         "mouseout" : true,
6022         /**
6023          * @event rowclass
6024          * Fires when a row is rendered, so you can change add a style to it.
6025          * @param {Roo.bootstrap.Table} this
6026          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6027          */
6028         'rowclass' : true,
6029           /**
6030          * @event rowsrendered
6031          * Fires when all the  rows have been rendered
6032          * @param {Roo.bootstrap.Table} this
6033          */
6034         'rowsrendered' : true,
6035         /**
6036          * @event contextmenu
6037          * The raw contextmenu event for the entire grid.
6038          * @param {Roo.EventObject} e
6039          */
6040         "contextmenu" : true,
6041         /**
6042          * @event rowcontextmenu
6043          * Fires when a row is right clicked
6044          * @param {Roo.bootstrap.Table} this
6045          * @param {Number} rowIndex
6046          * @param {Roo.EventObject} e
6047          */
6048         "rowcontextmenu" : true,
6049         /**
6050          * @event cellcontextmenu
6051          * Fires when a cell is right clicked
6052          * @param {Roo.bootstrap.Table} this
6053          * @param {Number} rowIndex
6054          * @param {Number} cellIndex
6055          * @param {Roo.EventObject} e
6056          */
6057          "cellcontextmenu" : true,
6058          /**
6059          * @event headercontextmenu
6060          * Fires when a header is right clicked
6061          * @param {Roo.bootstrap.Table} this
6062          * @param {Number} columnIndex
6063          * @param {Roo.EventObject} e
6064          */
6065         "headercontextmenu" : true
6066     });
6067 };
6068
6069 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6070     
6071     cls: false,
6072     align: false,
6073     bgcolor: false,
6074     border: false,
6075     cellpadding: false,
6076     cellspacing: false,
6077     frame: false,
6078     rules: false,
6079     sortable: false,
6080     summary: false,
6081     width: false,
6082     striped : false,
6083     scrollBody : false,
6084     bordered: false,
6085     hover:  false,
6086     condensed : false,
6087     responsive : false,
6088     sm : false,
6089     cm : false,
6090     store : false,
6091     loadMask : false,
6092     footerShow : true,
6093     headerShow : true,
6094   
6095     rowSelection : false,
6096     cellSelection : false,
6097     layout : false,
6098     
6099     // Roo.Element - the tbody
6100     mainBody: false,
6101     // Roo.Element - thead element
6102     mainHead: false,
6103     
6104     container: false, // used by gridpanel...
6105     
6106     lazyLoad : false,
6107     
6108     CSS : Roo.util.CSS,
6109     
6110     auto_hide_footer : false,
6111     
6112     getAutoCreate : function()
6113     {
6114         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6115         
6116         cfg = {
6117             tag: 'table',
6118             cls : 'table',
6119             cn : []
6120         };
6121         if (this.scrollBody) {
6122             cfg.cls += ' table-body-fixed';
6123         }    
6124         if (this.striped) {
6125             cfg.cls += ' table-striped';
6126         }
6127         
6128         if (this.hover) {
6129             cfg.cls += ' table-hover';
6130         }
6131         if (this.bordered) {
6132             cfg.cls += ' table-bordered';
6133         }
6134         if (this.condensed) {
6135             cfg.cls += ' table-condensed';
6136         }
6137         if (this.responsive) {
6138             cfg.cls += ' table-responsive';
6139         }
6140         
6141         if (this.cls) {
6142             cfg.cls+=  ' ' +this.cls;
6143         }
6144         
6145         // this lot should be simplifed...
6146         var _t = this;
6147         var cp = [
6148             'align',
6149             'bgcolor',
6150             'border',
6151             'cellpadding',
6152             'cellspacing',
6153             'frame',
6154             'rules',
6155             'sortable',
6156             'summary',
6157             'width'
6158         ].forEach(function(k) {
6159             if (_t[k]) {
6160                 cfg[k] = _t[k];
6161             }
6162         });
6163         
6164         
6165         if (this.layout) {
6166             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6167         }
6168         
6169         if(this.store || this.cm){
6170             if(this.headerShow){
6171                 cfg.cn.push(this.renderHeader());
6172             }
6173             
6174             cfg.cn.push(this.renderBody());
6175             
6176             if(this.footerShow){
6177                 cfg.cn.push(this.renderFooter());
6178             }
6179             // where does this come from?
6180             //cfg.cls+=  ' TableGrid';
6181         }
6182         
6183         return { cn : [ cfg ] };
6184     },
6185     
6186     initEvents : function()
6187     {   
6188         if(!this.store || !this.cm){
6189             return;
6190         }
6191         if (this.selModel) {
6192             this.selModel.initEvents();
6193         }
6194         
6195         
6196         //Roo.log('initEvents with ds!!!!');
6197         
6198         this.mainBody = this.el.select('tbody', true).first();
6199         this.mainHead = this.el.select('thead', true).first();
6200         this.mainFoot = this.el.select('tfoot', true).first();
6201         
6202         
6203         
6204         var _this = this;
6205         
6206         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6207             e.on('click', _this.sort, _this);
6208         });
6209         
6210         this.mainBody.on("click", this.onClick, this);
6211         this.mainBody.on("dblclick", this.onDblClick, this);
6212         
6213         // why is this done????? = it breaks dialogs??
6214         //this.parent().el.setStyle('position', 'relative');
6215         
6216         
6217         if (this.footer) {
6218             this.footer.parentId = this.id;
6219             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6220             
6221             if(this.lazyLoad){
6222                 this.el.select('tfoot tr td').first().addClass('hide');
6223             }
6224         } 
6225         
6226         if(this.loadMask) {
6227             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6228         }
6229         
6230         this.store.on('load', this.onLoad, this);
6231         this.store.on('beforeload', this.onBeforeLoad, this);
6232         this.store.on('update', this.onUpdate, this);
6233         this.store.on('add', this.onAdd, this);
6234         this.store.on("clear", this.clear, this);
6235         
6236         this.el.on("contextmenu", this.onContextMenu, this);
6237         
6238         this.mainBody.on('scroll', this.onBodyScroll, this);
6239         
6240         this.cm.on("headerchange", this.onHeaderChange, this);
6241         
6242         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6243         
6244     },
6245     
6246     onContextMenu : function(e, t)
6247     {
6248         this.processEvent("contextmenu", e);
6249     },
6250     
6251     processEvent : function(name, e)
6252     {
6253         if (name != 'touchstart' ) {
6254             this.fireEvent(name, e);    
6255         }
6256         
6257         var t = e.getTarget();
6258         
6259         var cell = Roo.get(t);
6260         
6261         if(!cell){
6262             return;
6263         }
6264         
6265         if(cell.findParent('tfoot', false, true)){
6266             return;
6267         }
6268         
6269         if(cell.findParent('thead', false, true)){
6270             
6271             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6272                 cell = Roo.get(t).findParent('th', false, true);
6273                 if (!cell) {
6274                     Roo.log("failed to find th in thead?");
6275                     Roo.log(e.getTarget());
6276                     return;
6277                 }
6278             }
6279             
6280             var cellIndex = cell.dom.cellIndex;
6281             
6282             var ename = name == 'touchstart' ? 'click' : name;
6283             this.fireEvent("header" + ename, this, cellIndex, e);
6284             
6285             return;
6286         }
6287         
6288         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6289             cell = Roo.get(t).findParent('td', false, true);
6290             if (!cell) {
6291                 Roo.log("failed to find th in tbody?");
6292                 Roo.log(e.getTarget());
6293                 return;
6294             }
6295         }
6296         
6297         var row = cell.findParent('tr', false, true);
6298         var cellIndex = cell.dom.cellIndex;
6299         var rowIndex = row.dom.rowIndex - 1;
6300         
6301         if(row !== false){
6302             
6303             this.fireEvent("row" + name, this, rowIndex, e);
6304             
6305             if(cell !== false){
6306             
6307                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6308             }
6309         }
6310         
6311     },
6312     
6313     onMouseover : function(e, el)
6314     {
6315         var cell = Roo.get(el);
6316         
6317         if(!cell){
6318             return;
6319         }
6320         
6321         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6322             cell = cell.findParent('td', false, true);
6323         }
6324         
6325         var row = cell.findParent('tr', false, true);
6326         var cellIndex = cell.dom.cellIndex;
6327         var rowIndex = row.dom.rowIndex - 1; // start from 0
6328         
6329         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6330         
6331     },
6332     
6333     onMouseout : function(e, el)
6334     {
6335         var cell = Roo.get(el);
6336         
6337         if(!cell){
6338             return;
6339         }
6340         
6341         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6342             cell = cell.findParent('td', false, true);
6343         }
6344         
6345         var row = cell.findParent('tr', false, true);
6346         var cellIndex = cell.dom.cellIndex;
6347         var rowIndex = row.dom.rowIndex - 1; // start from 0
6348         
6349         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6350         
6351     },
6352     
6353     onClick : function(e, el)
6354     {
6355         var cell = Roo.get(el);
6356         
6357         if(!cell || (!this.cellSelection && !this.rowSelection)){
6358             return;
6359         }
6360         
6361         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6362             cell = cell.findParent('td', false, true);
6363         }
6364         
6365         if(!cell || typeof(cell) == 'undefined'){
6366             return;
6367         }
6368         
6369         var row = cell.findParent('tr', false, true);
6370         
6371         if(!row || typeof(row) == 'undefined'){
6372             return;
6373         }
6374         
6375         var cellIndex = cell.dom.cellIndex;
6376         var rowIndex = this.getRowIndex(row);
6377         
6378         // why??? - should these not be based on SelectionModel?
6379         if(this.cellSelection){
6380             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6381         }
6382         
6383         if(this.rowSelection){
6384             this.fireEvent('rowclick', this, row, rowIndex, e);
6385         }
6386         
6387         
6388     },
6389         
6390     onDblClick : function(e,el)
6391     {
6392         var cell = Roo.get(el);
6393         
6394         if(!cell || (!this.cellSelection && !this.rowSelection)){
6395             return;
6396         }
6397         
6398         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6399             cell = cell.findParent('td', false, true);
6400         }
6401         
6402         if(!cell || typeof(cell) == 'undefined'){
6403             return;
6404         }
6405         
6406         var row = cell.findParent('tr', false, true);
6407         
6408         if(!row || typeof(row) == 'undefined'){
6409             return;
6410         }
6411         
6412         var cellIndex = cell.dom.cellIndex;
6413         var rowIndex = this.getRowIndex(row);
6414         
6415         if(this.cellSelection){
6416             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6417         }
6418         
6419         if(this.rowSelection){
6420             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6421         }
6422     },
6423     
6424     sort : function(e,el)
6425     {
6426         var col = Roo.get(el);
6427         
6428         if(!col.hasClass('sortable')){
6429             return;
6430         }
6431         
6432         var sort = col.attr('sort');
6433         var dir = 'ASC';
6434         
6435         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6436             dir = 'DESC';
6437         }
6438         
6439         this.store.sortInfo = {field : sort, direction : dir};
6440         
6441         if (this.footer) {
6442             Roo.log("calling footer first");
6443             this.footer.onClick('first');
6444         } else {
6445         
6446             this.store.load({ params : { start : 0 } });
6447         }
6448     },
6449     
6450     renderHeader : function()
6451     {
6452         var header = {
6453             tag: 'thead',
6454             cn : []
6455         };
6456         
6457         var cm = this.cm;
6458         this.totalWidth = 0;
6459         
6460         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6461             
6462             var config = cm.config[i];
6463             
6464             var c = {
6465                 tag: 'th',
6466                 cls : 'x-hcol-' + i,
6467                 style : '',
6468                 html: cm.getColumnHeader(i)
6469             };
6470             
6471             var hh = '';
6472             
6473             if(typeof(config.sortable) != 'undefined' && config.sortable){
6474                 c.cls = 'sortable';
6475                 c.html = '<i class="glyphicon"></i>' + c.html;
6476             }
6477             
6478             if(typeof(config.lgHeader) != 'undefined'){
6479                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6480             }
6481             
6482             if(typeof(config.mdHeader) != 'undefined'){
6483                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6484             }
6485             
6486             if(typeof(config.smHeader) != 'undefined'){
6487                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6488             }
6489             
6490             if(typeof(config.xsHeader) != 'undefined'){
6491                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6492             }
6493             
6494             if(hh.length){
6495                 c.html = hh;
6496             }
6497             
6498             if(typeof(config.tooltip) != 'undefined'){
6499                 c.tooltip = config.tooltip;
6500             }
6501             
6502             if(typeof(config.colspan) != 'undefined'){
6503                 c.colspan = config.colspan;
6504             }
6505             
6506             if(typeof(config.hidden) != 'undefined' && config.hidden){
6507                 c.style += ' display:none;';
6508             }
6509             
6510             if(typeof(config.dataIndex) != 'undefined'){
6511                 c.sort = config.dataIndex;
6512             }
6513             
6514            
6515             
6516             if(typeof(config.align) != 'undefined' && config.align.length){
6517                 c.style += ' text-align:' + config.align + ';';
6518             }
6519             
6520             if(typeof(config.width) != 'undefined'){
6521                 c.style += ' width:' + config.width + 'px;';
6522                 this.totalWidth += config.width;
6523             } else {
6524                 this.totalWidth += 100; // assume minimum of 100 per column?
6525             }
6526             
6527             if(typeof(config.cls) != 'undefined'){
6528                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6529             }
6530             
6531             ['xs','sm','md','lg'].map(function(size){
6532                 
6533                 if(typeof(config[size]) == 'undefined'){
6534                     return;
6535                 }
6536                 
6537                 if (!config[size]) { // 0 = hidden
6538                     c.cls += ' hidden-' + size;
6539                     return;
6540                 }
6541                 
6542                 c.cls += ' col-' + size + '-' + config[size];
6543
6544             });
6545             
6546             header.cn.push(c)
6547         }
6548         
6549         return header;
6550     },
6551     
6552     renderBody : function()
6553     {
6554         var body = {
6555             tag: 'tbody',
6556             cn : [
6557                 {
6558                     tag: 'tr',
6559                     cn : [
6560                         {
6561                             tag : 'td',
6562                             colspan :  this.cm.getColumnCount()
6563                         }
6564                     ]
6565                 }
6566             ]
6567         };
6568         
6569         return body;
6570     },
6571     
6572     renderFooter : function()
6573     {
6574         var footer = {
6575             tag: 'tfoot',
6576             cn : [
6577                 {
6578                     tag: 'tr',
6579                     cn : [
6580                         {
6581                             tag : 'td',
6582                             colspan :  this.cm.getColumnCount()
6583                         }
6584                     ]
6585                 }
6586             ]
6587         };
6588         
6589         return footer;
6590     },
6591     
6592     
6593     
6594     onLoad : function()
6595     {
6596 //        Roo.log('ds onload');
6597         this.clear();
6598         
6599         var _this = this;
6600         var cm = this.cm;
6601         var ds = this.store;
6602         
6603         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6604             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6605             if (_this.store.sortInfo) {
6606                     
6607                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6608                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6609                 }
6610                 
6611                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6612                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6613                 }
6614             }
6615         });
6616         
6617         var tbody =  this.mainBody;
6618               
6619         if(ds.getCount() > 0){
6620             ds.data.each(function(d,rowIndex){
6621                 var row =  this.renderRow(cm, ds, rowIndex);
6622                 
6623                 tbody.createChild(row);
6624                 
6625                 var _this = this;
6626                 
6627                 if(row.cellObjects.length){
6628                     Roo.each(row.cellObjects, function(r){
6629                         _this.renderCellObject(r);
6630                     })
6631                 }
6632                 
6633             }, this);
6634         }
6635         
6636         var tfoot = this.el.select('tfoot', true).first();
6637         
6638         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6639             
6640             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6641             
6642             var total = this.ds.getTotalCount();
6643             
6644             if(this.footer.pageSize < total){
6645                 this.mainFoot.show();
6646             }
6647         }
6648         
6649         Roo.each(this.el.select('tbody td', true).elements, function(e){
6650             e.on('mouseover', _this.onMouseover, _this);
6651         });
6652         
6653         Roo.each(this.el.select('tbody td', true).elements, function(e){
6654             e.on('mouseout', _this.onMouseout, _this);
6655         });
6656         this.fireEvent('rowsrendered', this);
6657         
6658         this.autoSize();
6659     },
6660     
6661     
6662     onUpdate : function(ds,record)
6663     {
6664         this.refreshRow(record);
6665         this.autoSize();
6666     },
6667     
6668     onRemove : function(ds, record, index, isUpdate){
6669         if(isUpdate !== true){
6670             this.fireEvent("beforerowremoved", this, index, record);
6671         }
6672         var bt = this.mainBody.dom;
6673         
6674         var rows = this.el.select('tbody > tr', true).elements;
6675         
6676         if(typeof(rows[index]) != 'undefined'){
6677             bt.removeChild(rows[index].dom);
6678         }
6679         
6680 //        if(bt.rows[index]){
6681 //            bt.removeChild(bt.rows[index]);
6682 //        }
6683         
6684         if(isUpdate !== true){
6685             //this.stripeRows(index);
6686             //this.syncRowHeights(index, index);
6687             //this.layout();
6688             this.fireEvent("rowremoved", this, index, record);
6689         }
6690     },
6691     
6692     onAdd : function(ds, records, rowIndex)
6693     {
6694         //Roo.log('on Add called');
6695         // - note this does not handle multiple adding very well..
6696         var bt = this.mainBody.dom;
6697         for (var i =0 ; i < records.length;i++) {
6698             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6699             //Roo.log(records[i]);
6700             //Roo.log(this.store.getAt(rowIndex+i));
6701             this.insertRow(this.store, rowIndex + i, false);
6702             return;
6703         }
6704         
6705     },
6706     
6707     
6708     refreshRow : function(record){
6709         var ds = this.store, index;
6710         if(typeof record == 'number'){
6711             index = record;
6712             record = ds.getAt(index);
6713         }else{
6714             index = ds.indexOf(record);
6715         }
6716         this.insertRow(ds, index, true);
6717         this.autoSize();
6718         this.onRemove(ds, record, index+1, true);
6719         this.autoSize();
6720         //this.syncRowHeights(index, index);
6721         //this.layout();
6722         this.fireEvent("rowupdated", this, index, record);
6723     },
6724     
6725     insertRow : function(dm, rowIndex, isUpdate){
6726         
6727         if(!isUpdate){
6728             this.fireEvent("beforerowsinserted", this, rowIndex);
6729         }
6730             //var s = this.getScrollState();
6731         var row = this.renderRow(this.cm, this.store, rowIndex);
6732         // insert before rowIndex..
6733         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6734         
6735         var _this = this;
6736                 
6737         if(row.cellObjects.length){
6738             Roo.each(row.cellObjects, function(r){
6739                 _this.renderCellObject(r);
6740             })
6741         }
6742             
6743         if(!isUpdate){
6744             this.fireEvent("rowsinserted", this, rowIndex);
6745             //this.syncRowHeights(firstRow, lastRow);
6746             //this.stripeRows(firstRow);
6747             //this.layout();
6748         }
6749         
6750     },
6751     
6752     
6753     getRowDom : function(rowIndex)
6754     {
6755         var rows = this.el.select('tbody > tr', true).elements;
6756         
6757         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6758         
6759     },
6760     // returns the object tree for a tr..
6761   
6762     
6763     renderRow : function(cm, ds, rowIndex) 
6764     {
6765         var d = ds.getAt(rowIndex);
6766         
6767         var row = {
6768             tag : 'tr',
6769             cls : 'x-row-' + rowIndex,
6770             cn : []
6771         };
6772             
6773         var cellObjects = [];
6774         
6775         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6776             var config = cm.config[i];
6777             
6778             var renderer = cm.getRenderer(i);
6779             var value = '';
6780             var id = false;
6781             
6782             if(typeof(renderer) !== 'undefined'){
6783                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6784             }
6785             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6786             // and are rendered into the cells after the row is rendered - using the id for the element.
6787             
6788             if(typeof(value) === 'object'){
6789                 id = Roo.id();
6790                 cellObjects.push({
6791                     container : id,
6792                     cfg : value 
6793                 })
6794             }
6795             
6796             var rowcfg = {
6797                 record: d,
6798                 rowIndex : rowIndex,
6799                 colIndex : i,
6800                 rowClass : ''
6801             };
6802
6803             this.fireEvent('rowclass', this, rowcfg);
6804             
6805             var td = {
6806                 tag: 'td',
6807                 cls : rowcfg.rowClass + ' x-col-' + i,
6808                 style: '',
6809                 html: (typeof(value) === 'object') ? '' : value
6810             };
6811             
6812             if (id) {
6813                 td.id = id;
6814             }
6815             
6816             if(typeof(config.colspan) != 'undefined'){
6817                 td.colspan = config.colspan;
6818             }
6819             
6820             if(typeof(config.hidden) != 'undefined' && config.hidden){
6821                 td.style += ' display:none;';
6822             }
6823             
6824             if(typeof(config.align) != 'undefined' && config.align.length){
6825                 td.style += ' text-align:' + config.align + ';';
6826             }
6827             if(typeof(config.valign) != 'undefined' && config.valign.length){
6828                 td.style += ' vertical-align:' + config.valign + ';';
6829             }
6830             
6831             if(typeof(config.width) != 'undefined'){
6832                 td.style += ' width:' +  config.width + 'px;';
6833             }
6834             
6835             if(typeof(config.cursor) != 'undefined'){
6836                 td.style += ' cursor:' +  config.cursor + ';';
6837             }
6838             
6839             if(typeof(config.cls) != 'undefined'){
6840                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6841             }
6842             
6843             ['xs','sm','md','lg'].map(function(size){
6844                 
6845                 if(typeof(config[size]) == 'undefined'){
6846                     return;
6847                 }
6848                 
6849                 if (!config[size]) { // 0 = hidden
6850                     td.cls += ' hidden-' + size;
6851                     return;
6852                 }
6853                 
6854                 td.cls += ' col-' + size + '-' + config[size];
6855
6856             });
6857             
6858             row.cn.push(td);
6859            
6860         }
6861         
6862         row.cellObjects = cellObjects;
6863         
6864         return row;
6865           
6866     },
6867     
6868     
6869     
6870     onBeforeLoad : function()
6871     {
6872         
6873     },
6874      /**
6875      * Remove all rows
6876      */
6877     clear : function()
6878     {
6879         this.el.select('tbody', true).first().dom.innerHTML = '';
6880     },
6881     /**
6882      * Show or hide a row.
6883      * @param {Number} rowIndex to show or hide
6884      * @param {Boolean} state hide
6885      */
6886     setRowVisibility : function(rowIndex, state)
6887     {
6888         var bt = this.mainBody.dom;
6889         
6890         var rows = this.el.select('tbody > tr', true).elements;
6891         
6892         if(typeof(rows[rowIndex]) == 'undefined'){
6893             return;
6894         }
6895         rows[rowIndex].dom.style.display = state ? '' : 'none';
6896     },
6897     
6898     
6899     getSelectionModel : function(){
6900         if(!this.selModel){
6901             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6902         }
6903         return this.selModel;
6904     },
6905     /*
6906      * Render the Roo.bootstrap object from renderder
6907      */
6908     renderCellObject : function(r)
6909     {
6910         var _this = this;
6911         
6912         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6913         
6914         var t = r.cfg.render(r.container);
6915         
6916         if(r.cfg.cn){
6917             Roo.each(r.cfg.cn, function(c){
6918                 var child = {
6919                     container: t.getChildContainer(),
6920                     cfg: c
6921                 };
6922                 _this.renderCellObject(child);
6923             })
6924         }
6925     },
6926     
6927     getRowIndex : function(row)
6928     {
6929         var rowIndex = -1;
6930         
6931         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6932             if(el != row){
6933                 return;
6934             }
6935             
6936             rowIndex = index;
6937         });
6938         
6939         return rowIndex;
6940     },
6941      /**
6942      * Returns the grid's underlying element = used by panel.Grid
6943      * @return {Element} The element
6944      */
6945     getGridEl : function(){
6946         return this.el;
6947     },
6948      /**
6949      * Forces a resize - used by panel.Grid
6950      * @return {Element} The element
6951      */
6952     autoSize : function()
6953     {
6954         //var ctr = Roo.get(this.container.dom.parentElement);
6955         var ctr = Roo.get(this.el.dom);
6956         
6957         var thd = this.getGridEl().select('thead',true).first();
6958         var tbd = this.getGridEl().select('tbody', true).first();
6959         var tfd = this.getGridEl().select('tfoot', true).first();
6960         
6961         var cw = ctr.getWidth();
6962         
6963         if (tbd) {
6964             
6965             tbd.setSize(ctr.getWidth(),
6966                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6967             );
6968             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6969             cw -= barsize;
6970         }
6971         cw = Math.max(cw, this.totalWidth);
6972         this.getGridEl().select('tr',true).setWidth(cw);
6973         // resize 'expandable coloumn?
6974         
6975         return; // we doe not have a view in this design..
6976         
6977     },
6978     onBodyScroll: function()
6979     {
6980         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6981         if(this.mainHead){
6982             this.mainHead.setStyle({
6983                 'position' : 'relative',
6984                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6985             });
6986         }
6987         
6988         if(this.lazyLoad){
6989             
6990             var scrollHeight = this.mainBody.dom.scrollHeight;
6991             
6992             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6993             
6994             var height = this.mainBody.getHeight();
6995             
6996             if(scrollHeight - height == scrollTop) {
6997                 
6998                 var total = this.ds.getTotalCount();
6999                 
7000                 if(this.footer.cursor + this.footer.pageSize < total){
7001                     
7002                     this.footer.ds.load({
7003                         params : {
7004                             start : this.footer.cursor + this.footer.pageSize,
7005                             limit : this.footer.pageSize
7006                         },
7007                         add : true
7008                     });
7009                 }
7010             }
7011             
7012         }
7013     },
7014     
7015     onHeaderChange : function()
7016     {
7017         var header = this.renderHeader();
7018         var table = this.el.select('table', true).first();
7019         
7020         this.mainHead.remove();
7021         this.mainHead = table.createChild(header, this.mainBody, false);
7022     },
7023     
7024     onHiddenChange : function(colModel, colIndex, hidden)
7025     {
7026         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7027         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7028         
7029         this.CSS.updateRule(thSelector, "display", "");
7030         this.CSS.updateRule(tdSelector, "display", "");
7031         
7032         if(hidden){
7033             this.CSS.updateRule(thSelector, "display", "none");
7034             this.CSS.updateRule(tdSelector, "display", "none");
7035         }
7036         
7037         this.onHeaderChange();
7038         this.onLoad();
7039         
7040     }
7041     
7042 });
7043
7044  
7045
7046  /*
7047  * - LGPL
7048  *
7049  * table cell
7050  * 
7051  */
7052
7053 /**
7054  * @class Roo.bootstrap.TableCell
7055  * @extends Roo.bootstrap.Component
7056  * Bootstrap TableCell class
7057  * @cfg {String} html cell contain text
7058  * @cfg {String} cls cell class
7059  * @cfg {String} tag cell tag (td|th) default td
7060  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7061  * @cfg {String} align Aligns the content in a cell
7062  * @cfg {String} axis Categorizes cells
7063  * @cfg {String} bgcolor Specifies the background color of a cell
7064  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7065  * @cfg {Number} colspan Specifies the number of columns a cell should span
7066  * @cfg {String} headers Specifies one or more header cells a cell is related to
7067  * @cfg {Number} height Sets the height of a cell
7068  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7069  * @cfg {Number} rowspan Sets the number of rows a cell should span
7070  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7071  * @cfg {String} valign Vertical aligns the content in a cell
7072  * @cfg {Number} width Specifies the width of a cell
7073  * 
7074  * @constructor
7075  * Create a new TableCell
7076  * @param {Object} config The config object
7077  */
7078
7079 Roo.bootstrap.TableCell = function(config){
7080     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7081 };
7082
7083 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7084     
7085     html: false,
7086     cls: false,
7087     tag: false,
7088     abbr: false,
7089     align: false,
7090     axis: false,
7091     bgcolor: false,
7092     charoff: false,
7093     colspan: false,
7094     headers: false,
7095     height: false,
7096     nowrap: false,
7097     rowspan: false,
7098     scope: false,
7099     valign: false,
7100     width: false,
7101     
7102     
7103     getAutoCreate : function(){
7104         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7105         
7106         cfg = {
7107             tag: 'td'
7108         };
7109         
7110         if(this.tag){
7111             cfg.tag = this.tag;
7112         }
7113         
7114         if (this.html) {
7115             cfg.html=this.html
7116         }
7117         if (this.cls) {
7118             cfg.cls=this.cls
7119         }
7120         if (this.abbr) {
7121             cfg.abbr=this.abbr
7122         }
7123         if (this.align) {
7124             cfg.align=this.align
7125         }
7126         if (this.axis) {
7127             cfg.axis=this.axis
7128         }
7129         if (this.bgcolor) {
7130             cfg.bgcolor=this.bgcolor
7131         }
7132         if (this.charoff) {
7133             cfg.charoff=this.charoff
7134         }
7135         if (this.colspan) {
7136             cfg.colspan=this.colspan
7137         }
7138         if (this.headers) {
7139             cfg.headers=this.headers
7140         }
7141         if (this.height) {
7142             cfg.height=this.height
7143         }
7144         if (this.nowrap) {
7145             cfg.nowrap=this.nowrap
7146         }
7147         if (this.rowspan) {
7148             cfg.rowspan=this.rowspan
7149         }
7150         if (this.scope) {
7151             cfg.scope=this.scope
7152         }
7153         if (this.valign) {
7154             cfg.valign=this.valign
7155         }
7156         if (this.width) {
7157             cfg.width=this.width
7158         }
7159         
7160         
7161         return cfg;
7162     }
7163    
7164 });
7165
7166  
7167
7168  /*
7169  * - LGPL
7170  *
7171  * table row
7172  * 
7173  */
7174
7175 /**
7176  * @class Roo.bootstrap.TableRow
7177  * @extends Roo.bootstrap.Component
7178  * Bootstrap TableRow class
7179  * @cfg {String} cls row class
7180  * @cfg {String} align Aligns the content in a table row
7181  * @cfg {String} bgcolor Specifies a background color for a table row
7182  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7183  * @cfg {String} valign Vertical aligns the content in a table row
7184  * 
7185  * @constructor
7186  * Create a new TableRow
7187  * @param {Object} config The config object
7188  */
7189
7190 Roo.bootstrap.TableRow = function(config){
7191     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7192 };
7193
7194 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7195     
7196     cls: false,
7197     align: false,
7198     bgcolor: false,
7199     charoff: false,
7200     valign: false,
7201     
7202     getAutoCreate : function(){
7203         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7204         
7205         cfg = {
7206             tag: 'tr'
7207         };
7208             
7209         if(this.cls){
7210             cfg.cls = this.cls;
7211         }
7212         if(this.align){
7213             cfg.align = this.align;
7214         }
7215         if(this.bgcolor){
7216             cfg.bgcolor = this.bgcolor;
7217         }
7218         if(this.charoff){
7219             cfg.charoff = this.charoff;
7220         }
7221         if(this.valign){
7222             cfg.valign = this.valign;
7223         }
7224         
7225         return cfg;
7226     }
7227    
7228 });
7229
7230  
7231
7232  /*
7233  * - LGPL
7234  *
7235  * table body
7236  * 
7237  */
7238
7239 /**
7240  * @class Roo.bootstrap.TableBody
7241  * @extends Roo.bootstrap.Component
7242  * Bootstrap TableBody class
7243  * @cfg {String} cls element class
7244  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7245  * @cfg {String} align Aligns the content inside the element
7246  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7247  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7248  * 
7249  * @constructor
7250  * Create a new TableBody
7251  * @param {Object} config The config object
7252  */
7253
7254 Roo.bootstrap.TableBody = function(config){
7255     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7256 };
7257
7258 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7259     
7260     cls: false,
7261     tag: false,
7262     align: false,
7263     charoff: false,
7264     valign: false,
7265     
7266     getAutoCreate : function(){
7267         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7268         
7269         cfg = {
7270             tag: 'tbody'
7271         };
7272             
7273         if (this.cls) {
7274             cfg.cls=this.cls
7275         }
7276         if(this.tag){
7277             cfg.tag = this.tag;
7278         }
7279         
7280         if(this.align){
7281             cfg.align = this.align;
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 //    initEvents : function()
7295 //    {
7296 //        
7297 //        if(!this.store){
7298 //            return;
7299 //        }
7300 //        
7301 //        this.store = Roo.factory(this.store, Roo.data);
7302 //        this.store.on('load', this.onLoad, this);
7303 //        
7304 //        this.store.load();
7305 //        
7306 //    },
7307 //    
7308 //    onLoad: function () 
7309 //    {   
7310 //        this.fireEvent('load', this);
7311 //    }
7312 //    
7313 //   
7314 });
7315
7316  
7317
7318  /*
7319  * Based on:
7320  * Ext JS Library 1.1.1
7321  * Copyright(c) 2006-2007, Ext JS, LLC.
7322  *
7323  * Originally Released Under LGPL - original licence link has changed is not relivant.
7324  *
7325  * Fork - LGPL
7326  * <script type="text/javascript">
7327  */
7328
7329 // as we use this in bootstrap.
7330 Roo.namespace('Roo.form');
7331  /**
7332  * @class Roo.form.Action
7333  * Internal Class used to handle form actions
7334  * @constructor
7335  * @param {Roo.form.BasicForm} el The form element or its id
7336  * @param {Object} config Configuration options
7337  */
7338
7339  
7340  
7341 // define the action interface
7342 Roo.form.Action = function(form, options){
7343     this.form = form;
7344     this.options = options || {};
7345 };
7346 /**
7347  * Client Validation Failed
7348  * @const 
7349  */
7350 Roo.form.Action.CLIENT_INVALID = 'client';
7351 /**
7352  * Server Validation Failed
7353  * @const 
7354  */
7355 Roo.form.Action.SERVER_INVALID = 'server';
7356  /**
7357  * Connect to Server Failed
7358  * @const 
7359  */
7360 Roo.form.Action.CONNECT_FAILURE = 'connect';
7361 /**
7362  * Reading Data from Server Failed
7363  * @const 
7364  */
7365 Roo.form.Action.LOAD_FAILURE = 'load';
7366
7367 Roo.form.Action.prototype = {
7368     type : 'default',
7369     failureType : undefined,
7370     response : undefined,
7371     result : undefined,
7372
7373     // interface method
7374     run : function(options){
7375
7376     },
7377
7378     // interface method
7379     success : function(response){
7380
7381     },
7382
7383     // interface method
7384     handleResponse : function(response){
7385
7386     },
7387
7388     // default connection failure
7389     failure : function(response){
7390         
7391         this.response = response;
7392         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7393         this.form.afterAction(this, false);
7394     },
7395
7396     processResponse : function(response){
7397         this.response = response;
7398         if(!response.responseText){
7399             return true;
7400         }
7401         this.result = this.handleResponse(response);
7402         return this.result;
7403     },
7404
7405     // utility functions used internally
7406     getUrl : function(appendParams){
7407         var url = this.options.url || this.form.url || this.form.el.dom.action;
7408         if(appendParams){
7409             var p = this.getParams();
7410             if(p){
7411                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7412             }
7413         }
7414         return url;
7415     },
7416
7417     getMethod : function(){
7418         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7419     },
7420
7421     getParams : function(){
7422         var bp = this.form.baseParams;
7423         var p = this.options.params;
7424         if(p){
7425             if(typeof p == "object"){
7426                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7427             }else if(typeof p == 'string' && bp){
7428                 p += '&' + Roo.urlEncode(bp);
7429             }
7430         }else if(bp){
7431             p = Roo.urlEncode(bp);
7432         }
7433         return p;
7434     },
7435
7436     createCallback : function(){
7437         return {
7438             success: this.success,
7439             failure: this.failure,
7440             scope: this,
7441             timeout: (this.form.timeout*1000),
7442             upload: this.form.fileUpload ? this.success : undefined
7443         };
7444     }
7445 };
7446
7447 Roo.form.Action.Submit = function(form, options){
7448     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7449 };
7450
7451 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7452     type : 'submit',
7453
7454     haveProgress : false,
7455     uploadComplete : false,
7456     
7457     // uploadProgress indicator.
7458     uploadProgress : function()
7459     {
7460         if (!this.form.progressUrl) {
7461             return;
7462         }
7463         
7464         if (!this.haveProgress) {
7465             Roo.MessageBox.progress("Uploading", "Uploading");
7466         }
7467         if (this.uploadComplete) {
7468            Roo.MessageBox.hide();
7469            return;
7470         }
7471         
7472         this.haveProgress = true;
7473    
7474         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7475         
7476         var c = new Roo.data.Connection();
7477         c.request({
7478             url : this.form.progressUrl,
7479             params: {
7480                 id : uid
7481             },
7482             method: 'GET',
7483             success : function(req){
7484                //console.log(data);
7485                 var rdata = false;
7486                 var edata;
7487                 try  {
7488                    rdata = Roo.decode(req.responseText)
7489                 } catch (e) {
7490                     Roo.log("Invalid data from server..");
7491                     Roo.log(edata);
7492                     return;
7493                 }
7494                 if (!rdata || !rdata.success) {
7495                     Roo.log(rdata);
7496                     Roo.MessageBox.alert(Roo.encode(rdata));
7497                     return;
7498                 }
7499                 var data = rdata.data;
7500                 
7501                 if (this.uploadComplete) {
7502                    Roo.MessageBox.hide();
7503                    return;
7504                 }
7505                    
7506                 if (data){
7507                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7508                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7509                     );
7510                 }
7511                 this.uploadProgress.defer(2000,this);
7512             },
7513        
7514             failure: function(data) {
7515                 Roo.log('progress url failed ');
7516                 Roo.log(data);
7517             },
7518             scope : this
7519         });
7520            
7521     },
7522     
7523     
7524     run : function()
7525     {
7526         // run get Values on the form, so it syncs any secondary forms.
7527         this.form.getValues();
7528         
7529         var o = this.options;
7530         var method = this.getMethod();
7531         var isPost = method == 'POST';
7532         if(o.clientValidation === false || this.form.isValid()){
7533             
7534             if (this.form.progressUrl) {
7535                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7536                     (new Date() * 1) + '' + Math.random());
7537                     
7538             } 
7539             
7540             
7541             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7542                 form:this.form.el.dom,
7543                 url:this.getUrl(!isPost),
7544                 method: method,
7545                 params:isPost ? this.getParams() : null,
7546                 isUpload: this.form.fileUpload
7547             }));
7548             
7549             this.uploadProgress();
7550
7551         }else if (o.clientValidation !== false){ // client validation failed
7552             this.failureType = Roo.form.Action.CLIENT_INVALID;
7553             this.form.afterAction(this, false);
7554         }
7555     },
7556
7557     success : function(response)
7558     {
7559         this.uploadComplete= true;
7560         if (this.haveProgress) {
7561             Roo.MessageBox.hide();
7562         }
7563         
7564         
7565         var result = this.processResponse(response);
7566         if(result === true || result.success){
7567             this.form.afterAction(this, true);
7568             return;
7569         }
7570         if(result.errors){
7571             this.form.markInvalid(result.errors);
7572             this.failureType = Roo.form.Action.SERVER_INVALID;
7573         }
7574         this.form.afterAction(this, false);
7575     },
7576     failure : function(response)
7577     {
7578         this.uploadComplete= true;
7579         if (this.haveProgress) {
7580             Roo.MessageBox.hide();
7581         }
7582         
7583         this.response = response;
7584         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7585         this.form.afterAction(this, false);
7586     },
7587     
7588     handleResponse : function(response){
7589         if(this.form.errorReader){
7590             var rs = this.form.errorReader.read(response);
7591             var errors = [];
7592             if(rs.records){
7593                 for(var i = 0, len = rs.records.length; i < len; i++) {
7594                     var r = rs.records[i];
7595                     errors[i] = r.data;
7596                 }
7597             }
7598             if(errors.length < 1){
7599                 errors = null;
7600             }
7601             return {
7602                 success : rs.success,
7603                 errors : errors
7604             };
7605         }
7606         var ret = false;
7607         try {
7608             ret = Roo.decode(response.responseText);
7609         } catch (e) {
7610             ret = {
7611                 success: false,
7612                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7613                 errors : []
7614             };
7615         }
7616         return ret;
7617         
7618     }
7619 });
7620
7621
7622 Roo.form.Action.Load = function(form, options){
7623     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7624     this.reader = this.form.reader;
7625 };
7626
7627 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7628     type : 'load',
7629
7630     run : function(){
7631         
7632         Roo.Ajax.request(Roo.apply(
7633                 this.createCallback(), {
7634                     method:this.getMethod(),
7635                     url:this.getUrl(false),
7636                     params:this.getParams()
7637         }));
7638     },
7639
7640     success : function(response){
7641         
7642         var result = this.processResponse(response);
7643         if(result === true || !result.success || !result.data){
7644             this.failureType = Roo.form.Action.LOAD_FAILURE;
7645             this.form.afterAction(this, false);
7646             return;
7647         }
7648         this.form.clearInvalid();
7649         this.form.setValues(result.data);
7650         this.form.afterAction(this, true);
7651     },
7652
7653     handleResponse : function(response){
7654         if(this.form.reader){
7655             var rs = this.form.reader.read(response);
7656             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7657             return {
7658                 success : rs.success,
7659                 data : data
7660             };
7661         }
7662         return Roo.decode(response.responseText);
7663     }
7664 });
7665
7666 Roo.form.Action.ACTION_TYPES = {
7667     'load' : Roo.form.Action.Load,
7668     'submit' : Roo.form.Action.Submit
7669 };/*
7670  * - LGPL
7671  *
7672  * form
7673  *
7674  */
7675
7676 /**
7677  * @class Roo.bootstrap.Form
7678  * @extends Roo.bootstrap.Component
7679  * Bootstrap Form class
7680  * @cfg {String} method  GET | POST (default POST)
7681  * @cfg {String} labelAlign top | left (default top)
7682  * @cfg {String} align left  | right - for navbars
7683  * @cfg {Boolean} loadMask load mask when submit (default true)
7684
7685  *
7686  * @constructor
7687  * Create a new Form
7688  * @param {Object} config The config object
7689  */
7690
7691
7692 Roo.bootstrap.Form = function(config){
7693     
7694     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7695     
7696     Roo.bootstrap.Form.popover.apply();
7697     
7698     this.addEvents({
7699         /**
7700          * @event clientvalidation
7701          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7702          * @param {Form} this
7703          * @param {Boolean} valid true if the form has passed client-side validation
7704          */
7705         clientvalidation: true,
7706         /**
7707          * @event beforeaction
7708          * Fires before any action is performed. Return false to cancel the action.
7709          * @param {Form} this
7710          * @param {Action} action The action to be performed
7711          */
7712         beforeaction: true,
7713         /**
7714          * @event actionfailed
7715          * Fires when an action fails.
7716          * @param {Form} this
7717          * @param {Action} action The action that failed
7718          */
7719         actionfailed : true,
7720         /**
7721          * @event actioncomplete
7722          * Fires when an action is completed.
7723          * @param {Form} this
7724          * @param {Action} action The action that completed
7725          */
7726         actioncomplete : true
7727     });
7728 };
7729
7730 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7731
7732      /**
7733      * @cfg {String} method
7734      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7735      */
7736     method : 'POST',
7737     /**
7738      * @cfg {String} url
7739      * The URL to use for form actions if one isn't supplied in the action options.
7740      */
7741     /**
7742      * @cfg {Boolean} fileUpload
7743      * Set to true if this form is a file upload.
7744      */
7745
7746     /**
7747      * @cfg {Object} baseParams
7748      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7749      */
7750
7751     /**
7752      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7753      */
7754     timeout: 30,
7755     /**
7756      * @cfg {Sting} align (left|right) for navbar forms
7757      */
7758     align : 'left',
7759
7760     // private
7761     activeAction : null,
7762
7763     /**
7764      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7765      * element by passing it or its id or mask the form itself by passing in true.
7766      * @type Mixed
7767      */
7768     waitMsgTarget : false,
7769
7770     loadMask : true,
7771     
7772     /**
7773      * @cfg {Boolean} errorMask (true|false) default false
7774      */
7775     errorMask : false,
7776     
7777     /**
7778      * @cfg {Number} maskOffset Default 100
7779      */
7780     maskOffset : 100,
7781     
7782     /**
7783      * @cfg {Boolean} maskBody
7784      */
7785     maskBody : false,
7786
7787     getAutoCreate : function(){
7788
7789         var cfg = {
7790             tag: 'form',
7791             method : this.method || 'POST',
7792             id : this.id || Roo.id(),
7793             cls : ''
7794         };
7795         if (this.parent().xtype.match(/^Nav/)) {
7796             cfg.cls = 'navbar-form navbar-' + this.align;
7797
7798         }
7799
7800         if (this.labelAlign == 'left' ) {
7801             cfg.cls += ' form-horizontal';
7802         }
7803
7804
7805         return cfg;
7806     },
7807     initEvents : function()
7808     {
7809         this.el.on('submit', this.onSubmit, this);
7810         // this was added as random key presses on the form where triggering form submit.
7811         this.el.on('keypress', function(e) {
7812             if (e.getCharCode() != 13) {
7813                 return true;
7814             }
7815             // we might need to allow it for textareas.. and some other items.
7816             // check e.getTarget().
7817
7818             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7819                 return true;
7820             }
7821
7822             Roo.log("keypress blocked");
7823
7824             e.preventDefault();
7825             return false;
7826         });
7827         
7828     },
7829     // private
7830     onSubmit : function(e){
7831         e.stopEvent();
7832     },
7833
7834      /**
7835      * Returns true if client-side validation on the form is successful.
7836      * @return Boolean
7837      */
7838     isValid : function(){
7839         var items = this.getItems();
7840         var valid = true;
7841         var target = false;
7842         
7843         items.each(function(f){
7844             
7845             if(f.validate()){
7846                 return;
7847             }
7848             
7849             Roo.log('invalid field: ' + f.name);
7850             
7851             valid = false;
7852
7853             if(!target && f.el.isVisible(true)){
7854                 target = f;
7855             }
7856            
7857         });
7858         
7859         if(this.errorMask && !valid){
7860             Roo.bootstrap.Form.popover.mask(this, target);
7861         }
7862         
7863         return valid;
7864     },
7865     
7866     /**
7867      * Returns true if any fields in this form have changed since their original load.
7868      * @return Boolean
7869      */
7870     isDirty : function(){
7871         var dirty = false;
7872         var items = this.getItems();
7873         items.each(function(f){
7874            if(f.isDirty()){
7875                dirty = true;
7876                return false;
7877            }
7878            return true;
7879         });
7880         return dirty;
7881     },
7882      /**
7883      * Performs a predefined action (submit or load) or custom actions you define on this form.
7884      * @param {String} actionName The name of the action type
7885      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7886      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7887      * accept other config options):
7888      * <pre>
7889 Property          Type             Description
7890 ----------------  ---------------  ----------------------------------------------------------------------------------
7891 url               String           The url for the action (defaults to the form's url)
7892 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7893 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7894 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7895                                    validate the form on the client (defaults to false)
7896      * </pre>
7897      * @return {BasicForm} this
7898      */
7899     doAction : function(action, options){
7900         if(typeof action == 'string'){
7901             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7902         }
7903         if(this.fireEvent('beforeaction', this, action) !== false){
7904             this.beforeAction(action);
7905             action.run.defer(100, action);
7906         }
7907         return this;
7908     },
7909
7910     // private
7911     beforeAction : function(action){
7912         var o = action.options;
7913         
7914         if(this.loadMask){
7915             
7916             if(this.maskBody){
7917                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7918             } else {
7919                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7920             }
7921         }
7922         // not really supported yet.. ??
7923
7924         //if(this.waitMsgTarget === true){
7925         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7926         //}else if(this.waitMsgTarget){
7927         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7928         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7929         //}else {
7930         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7931        // }
7932
7933     },
7934
7935     // private
7936     afterAction : function(action, success){
7937         this.activeAction = null;
7938         var o = action.options;
7939
7940         if(this.loadMask){
7941             
7942             if(this.maskBody){
7943                 Roo.get(document.body).unmask();
7944             } else {
7945                 this.el.unmask();
7946             }
7947         }
7948         
7949         //if(this.waitMsgTarget === true){
7950 //            this.el.unmask();
7951         //}else if(this.waitMsgTarget){
7952         //    this.waitMsgTarget.unmask();
7953         //}else{
7954         //    Roo.MessageBox.updateProgress(1);
7955         //    Roo.MessageBox.hide();
7956        // }
7957         //
7958         if(success){
7959             if(o.reset){
7960                 this.reset();
7961             }
7962             Roo.callback(o.success, o.scope, [this, action]);
7963             this.fireEvent('actioncomplete', this, action);
7964
7965         }else{
7966
7967             // failure condition..
7968             // we have a scenario where updates need confirming.
7969             // eg. if a locking scenario exists..
7970             // we look for { errors : { needs_confirm : true }} in the response.
7971             if (
7972                 (typeof(action.result) != 'undefined')  &&
7973                 (typeof(action.result.errors) != 'undefined')  &&
7974                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7975            ){
7976                 var _t = this;
7977                 Roo.log("not supported yet");
7978                  /*
7979
7980                 Roo.MessageBox.confirm(
7981                     "Change requires confirmation",
7982                     action.result.errorMsg,
7983                     function(r) {
7984                         if (r != 'yes') {
7985                             return;
7986                         }
7987                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7988                     }
7989
7990                 );
7991                 */
7992
7993
7994                 return;
7995             }
7996
7997             Roo.callback(o.failure, o.scope, [this, action]);
7998             // show an error message if no failed handler is set..
7999             if (!this.hasListener('actionfailed')) {
8000                 Roo.log("need to add dialog support");
8001                 /*
8002                 Roo.MessageBox.alert("Error",
8003                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8004                         action.result.errorMsg :
8005                         "Saving Failed, please check your entries or try again"
8006                 );
8007                 */
8008             }
8009
8010             this.fireEvent('actionfailed', this, action);
8011         }
8012
8013     },
8014     /**
8015      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8016      * @param {String} id The value to search for
8017      * @return Field
8018      */
8019     findField : function(id){
8020         var items = this.getItems();
8021         var field = items.get(id);
8022         if(!field){
8023              items.each(function(f){
8024                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8025                     field = f;
8026                     return false;
8027                 }
8028                 return true;
8029             });
8030         }
8031         return field || null;
8032     },
8033      /**
8034      * Mark fields in this form invalid in bulk.
8035      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8036      * @return {BasicForm} this
8037      */
8038     markInvalid : function(errors){
8039         if(errors instanceof Array){
8040             for(var i = 0, len = errors.length; i < len; i++){
8041                 var fieldError = errors[i];
8042                 var f = this.findField(fieldError.id);
8043                 if(f){
8044                     f.markInvalid(fieldError.msg);
8045                 }
8046             }
8047         }else{
8048             var field, id;
8049             for(id in errors){
8050                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8051                     field.markInvalid(errors[id]);
8052                 }
8053             }
8054         }
8055         //Roo.each(this.childForms || [], function (f) {
8056         //    f.markInvalid(errors);
8057         //});
8058
8059         return this;
8060     },
8061
8062     /**
8063      * Set values for fields in this form in bulk.
8064      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8065      * @return {BasicForm} this
8066      */
8067     setValues : function(values){
8068         if(values instanceof Array){ // array of objects
8069             for(var i = 0, len = values.length; i < len; i++){
8070                 var v = values[i];
8071                 var f = this.findField(v.id);
8072                 if(f){
8073                     f.setValue(v.value);
8074                     if(this.trackResetOnLoad){
8075                         f.originalValue = f.getValue();
8076                     }
8077                 }
8078             }
8079         }else{ // object hash
8080             var field, id;
8081             for(id in values){
8082                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8083
8084                     if (field.setFromData &&
8085                         field.valueField &&
8086                         field.displayField &&
8087                         // combos' with local stores can
8088                         // be queried via setValue()
8089                         // to set their value..
8090                         (field.store && !field.store.isLocal)
8091                         ) {
8092                         // it's a combo
8093                         var sd = { };
8094                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8095                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8096                         field.setFromData(sd);
8097
8098                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8099                         
8100                         field.setFromData(values);
8101                         
8102                     } else {
8103                         field.setValue(values[id]);
8104                     }
8105
8106
8107                     if(this.trackResetOnLoad){
8108                         field.originalValue = field.getValue();
8109                     }
8110                 }
8111             }
8112         }
8113
8114         //Roo.each(this.childForms || [], function (f) {
8115         //    f.setValues(values);
8116         //});
8117
8118         return this;
8119     },
8120
8121     /**
8122      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8123      * they are returned as an array.
8124      * @param {Boolean} asString
8125      * @return {Object}
8126      */
8127     getValues : function(asString){
8128         //if (this.childForms) {
8129             // copy values from the child forms
8130         //    Roo.each(this.childForms, function (f) {
8131         //        this.setValues(f.getValues());
8132         //    }, this);
8133         //}
8134
8135
8136
8137         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8138         if(asString === true){
8139             return fs;
8140         }
8141         return Roo.urlDecode(fs);
8142     },
8143
8144     /**
8145      * Returns the fields in this form as an object with key/value pairs.
8146      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8147      * @return {Object}
8148      */
8149     getFieldValues : function(with_hidden)
8150     {
8151         var items = this.getItems();
8152         var ret = {};
8153         items.each(function(f){
8154             
8155             if (!f.getName()) {
8156                 return;
8157             }
8158             
8159             var v = f.getValue();
8160             
8161             if (f.inputType =='radio') {
8162                 if (typeof(ret[f.getName()]) == 'undefined') {
8163                     ret[f.getName()] = ''; // empty..
8164                 }
8165
8166                 if (!f.el.dom.checked) {
8167                     return;
8168
8169                 }
8170                 v = f.el.dom.value;
8171
8172             }
8173             
8174             if(f.xtype == 'MoneyField'){
8175                 ret[f.currencyName] = f.getCurrency();
8176             }
8177
8178             // not sure if this supported any more..
8179             if ((typeof(v) == 'object') && f.getRawValue) {
8180                 v = f.getRawValue() ; // dates..
8181             }
8182             // combo boxes where name != hiddenName...
8183             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8184                 ret[f.name] = f.getRawValue();
8185             }
8186             ret[f.getName()] = v;
8187         });
8188
8189         return ret;
8190     },
8191
8192     /**
8193      * Clears all invalid messages in this form.
8194      * @return {BasicForm} this
8195      */
8196     clearInvalid : function(){
8197         var items = this.getItems();
8198
8199         items.each(function(f){
8200            f.clearInvalid();
8201         });
8202
8203         return this;
8204     },
8205
8206     /**
8207      * Resets this form.
8208      * @return {BasicForm} this
8209      */
8210     reset : function(){
8211         var items = this.getItems();
8212         items.each(function(f){
8213             f.reset();
8214         });
8215
8216         Roo.each(this.childForms || [], function (f) {
8217             f.reset();
8218         });
8219
8220
8221         return this;
8222     },
8223     
8224     getItems : function()
8225     {
8226         var r=new Roo.util.MixedCollection(false, function(o){
8227             return o.id || (o.id = Roo.id());
8228         });
8229         var iter = function(el) {
8230             if (el.inputEl) {
8231                 r.add(el);
8232             }
8233             if (!el.items) {
8234                 return;
8235             }
8236             Roo.each(el.items,function(e) {
8237                 iter(e);
8238             });
8239         };
8240
8241         iter(this);
8242         return r;
8243     },
8244     
8245     hideFields : function(items)
8246     {
8247         Roo.each(items, function(i){
8248             
8249             var f = this.findField(i);
8250             
8251             if(!f){
8252                 return;
8253             }
8254             
8255             if(f.xtype == 'DateField'){
8256                 f.setVisible(false);
8257                 return;
8258             }
8259             
8260             f.hide();
8261             
8262         }, this);
8263     },
8264     
8265     showFields : function(items)
8266     {
8267         Roo.each(items, function(i){
8268             
8269             var f = this.findField(i);
8270             
8271             if(!f){
8272                 return;
8273             }
8274             
8275             if(f.xtype == 'DateField'){
8276                 f.setVisible(true);
8277                 return;
8278             }
8279             
8280             f.show();
8281             
8282         }, this);
8283     }
8284
8285 });
8286
8287 Roo.apply(Roo.bootstrap.Form, {
8288     
8289     popover : {
8290         
8291         padding : 5,
8292         
8293         isApplied : false,
8294         
8295         isMasked : false,
8296         
8297         form : false,
8298         
8299         target : false,
8300         
8301         toolTip : false,
8302         
8303         intervalID : false,
8304         
8305         maskEl : false,
8306         
8307         apply : function()
8308         {
8309             if(this.isApplied){
8310                 return;
8311             }
8312             
8313             this.maskEl = {
8314                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8315                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8316                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8317                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8318             };
8319             
8320             this.maskEl.top.enableDisplayMode("block");
8321             this.maskEl.left.enableDisplayMode("block");
8322             this.maskEl.bottom.enableDisplayMode("block");
8323             this.maskEl.right.enableDisplayMode("block");
8324             
8325             this.toolTip = new Roo.bootstrap.Tooltip({
8326                 cls : 'roo-form-error-popover',
8327                 alignment : {
8328                     'left' : ['r-l', [-2,0], 'right'],
8329                     'right' : ['l-r', [2,0], 'left'],
8330                     'bottom' : ['tl-bl', [0,2], 'top'],
8331                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8332                 }
8333             });
8334             
8335             this.toolTip.render(Roo.get(document.body));
8336
8337             this.toolTip.el.enableDisplayMode("block");
8338             
8339             Roo.get(document.body).on('click', function(){
8340                 this.unmask();
8341             }, this);
8342             
8343             Roo.get(document.body).on('touchstart', function(){
8344                 this.unmask();
8345             }, this);
8346             
8347             this.isApplied = true
8348         },
8349         
8350         mask : function(form, target)
8351         {
8352             this.form = form;
8353             
8354             this.target = target;
8355             
8356             if(!this.form.errorMask || !target.el){
8357                 return;
8358             }
8359             
8360             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8361             
8362             Roo.log(scrollable);
8363             
8364             var ot = this.target.el.calcOffsetsTo(scrollable);
8365             
8366             var scrollTo = ot[1] - this.form.maskOffset;
8367             
8368             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8369             
8370             scrollable.scrollTo('top', scrollTo);
8371             
8372             var box = this.target.el.getBox();
8373             Roo.log(box);
8374             var zIndex = Roo.bootstrap.Modal.zIndex++;
8375
8376             
8377             this.maskEl.top.setStyle('position', 'absolute');
8378             this.maskEl.top.setStyle('z-index', zIndex);
8379             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8380             this.maskEl.top.setLeft(0);
8381             this.maskEl.top.setTop(0);
8382             this.maskEl.top.show();
8383             
8384             this.maskEl.left.setStyle('position', 'absolute');
8385             this.maskEl.left.setStyle('z-index', zIndex);
8386             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8387             this.maskEl.left.setLeft(0);
8388             this.maskEl.left.setTop(box.y - this.padding);
8389             this.maskEl.left.show();
8390
8391             this.maskEl.bottom.setStyle('position', 'absolute');
8392             this.maskEl.bottom.setStyle('z-index', zIndex);
8393             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8394             this.maskEl.bottom.setLeft(0);
8395             this.maskEl.bottom.setTop(box.bottom + this.padding);
8396             this.maskEl.bottom.show();
8397
8398             this.maskEl.right.setStyle('position', 'absolute');
8399             this.maskEl.right.setStyle('z-index', zIndex);
8400             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8401             this.maskEl.right.setLeft(box.right + this.padding);
8402             this.maskEl.right.setTop(box.y - this.padding);
8403             this.maskEl.right.show();
8404
8405             this.toolTip.bindEl = this.target.el;
8406
8407             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8408
8409             var tip = this.target.blankText;
8410
8411             if(this.target.getValue() !== '' ) {
8412                 
8413                 if (this.target.invalidText.length) {
8414                     tip = this.target.invalidText;
8415                 } else if (this.target.regexText.length){
8416                     tip = this.target.regexText;
8417                 }
8418             }
8419
8420             this.toolTip.show(tip);
8421
8422             this.intervalID = window.setInterval(function() {
8423                 Roo.bootstrap.Form.popover.unmask();
8424             }, 10000);
8425
8426             window.onwheel = function(){ return false;};
8427             
8428             (function(){ this.isMasked = true; }).defer(500, this);
8429             
8430         },
8431         
8432         unmask : function()
8433         {
8434             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8435                 return;
8436             }
8437             
8438             this.maskEl.top.setStyle('position', 'absolute');
8439             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8440             this.maskEl.top.hide();
8441
8442             this.maskEl.left.setStyle('position', 'absolute');
8443             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8444             this.maskEl.left.hide();
8445
8446             this.maskEl.bottom.setStyle('position', 'absolute');
8447             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8448             this.maskEl.bottom.hide();
8449
8450             this.maskEl.right.setStyle('position', 'absolute');
8451             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8452             this.maskEl.right.hide();
8453             
8454             this.toolTip.hide();
8455             
8456             this.toolTip.el.hide();
8457             
8458             window.onwheel = function(){ return true;};
8459             
8460             if(this.intervalID){
8461                 window.clearInterval(this.intervalID);
8462                 this.intervalID = false;
8463             }
8464             
8465             this.isMasked = false;
8466             
8467         }
8468         
8469     }
8470     
8471 });
8472
8473 /*
8474  * Based on:
8475  * Ext JS Library 1.1.1
8476  * Copyright(c) 2006-2007, Ext JS, LLC.
8477  *
8478  * Originally Released Under LGPL - original licence link has changed is not relivant.
8479  *
8480  * Fork - LGPL
8481  * <script type="text/javascript">
8482  */
8483 /**
8484  * @class Roo.form.VTypes
8485  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8486  * @singleton
8487  */
8488 Roo.form.VTypes = function(){
8489     // closure these in so they are only created once.
8490     var alpha = /^[a-zA-Z_]+$/;
8491     var alphanum = /^[a-zA-Z0-9_]+$/;
8492     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8493     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8494
8495     // All these messages and functions are configurable
8496     return {
8497         /**
8498          * The function used to validate email addresses
8499          * @param {String} value The email address
8500          */
8501         'email' : function(v){
8502             return email.test(v);
8503         },
8504         /**
8505          * The error text to display when the email validation function returns false
8506          * @type String
8507          */
8508         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8509         /**
8510          * The keystroke filter mask to be applied on email input
8511          * @type RegExp
8512          */
8513         'emailMask' : /[a-z0-9_\.\-@]/i,
8514
8515         /**
8516          * The function used to validate URLs
8517          * @param {String} value The URL
8518          */
8519         'url' : function(v){
8520             return url.test(v);
8521         },
8522         /**
8523          * The error text to display when the url validation function returns false
8524          * @type String
8525          */
8526         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8527         
8528         /**
8529          * The function used to validate alpha values
8530          * @param {String} value The value
8531          */
8532         'alpha' : function(v){
8533             return alpha.test(v);
8534         },
8535         /**
8536          * The error text to display when the alpha validation function returns false
8537          * @type String
8538          */
8539         'alphaText' : 'This field should only contain letters and _',
8540         /**
8541          * The keystroke filter mask to be applied on alpha input
8542          * @type RegExp
8543          */
8544         'alphaMask' : /[a-z_]/i,
8545
8546         /**
8547          * The function used to validate alphanumeric values
8548          * @param {String} value The value
8549          */
8550         'alphanum' : function(v){
8551             return alphanum.test(v);
8552         },
8553         /**
8554          * The error text to display when the alphanumeric validation function returns false
8555          * @type String
8556          */
8557         'alphanumText' : 'This field should only contain letters, numbers and _',
8558         /**
8559          * The keystroke filter mask to be applied on alphanumeric input
8560          * @type RegExp
8561          */
8562         'alphanumMask' : /[a-z0-9_]/i
8563     };
8564 }();/*
8565  * - LGPL
8566  *
8567  * Input
8568  * 
8569  */
8570
8571 /**
8572  * @class Roo.bootstrap.Input
8573  * @extends Roo.bootstrap.Component
8574  * Bootstrap Input class
8575  * @cfg {Boolean} disabled is it disabled
8576  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8577  * @cfg {String} name name of the input
8578  * @cfg {string} fieldLabel - the label associated
8579  * @cfg {string} placeholder - placeholder to put in text.
8580  * @cfg {string}  before - input group add on before
8581  * @cfg {string} after - input group add on after
8582  * @cfg {string} size - (lg|sm) or leave empty..
8583  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8584  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8585  * @cfg {Number} md colspan out of 12 for computer-sized screens
8586  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8587  * @cfg {string} value default value of the input
8588  * @cfg {Number} labelWidth set the width of label 
8589  * @cfg {Number} labellg set the width of label (1-12)
8590  * @cfg {Number} labelmd set the width of label (1-12)
8591  * @cfg {Number} labelsm set the width of label (1-12)
8592  * @cfg {Number} labelxs set the width of label (1-12)
8593  * @cfg {String} labelAlign (top|left)
8594  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8595  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8596  * @cfg {String} indicatorpos (left|right) default left
8597  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8598  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8599
8600  * @cfg {String} align (left|center|right) Default left
8601  * @cfg {Boolean} forceFeedback (true|false) Default false
8602  * 
8603  * @constructor
8604  * Create a new Input
8605  * @param {Object} config The config object
8606  */
8607
8608 Roo.bootstrap.Input = function(config){
8609     
8610     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8611     
8612     this.addEvents({
8613         /**
8614          * @event focus
8615          * Fires when this field receives input focus.
8616          * @param {Roo.form.Field} this
8617          */
8618         focus : true,
8619         /**
8620          * @event blur
8621          * Fires when this field loses input focus.
8622          * @param {Roo.form.Field} this
8623          */
8624         blur : true,
8625         /**
8626          * @event specialkey
8627          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8628          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8629          * @param {Roo.form.Field} this
8630          * @param {Roo.EventObject} e The event object
8631          */
8632         specialkey : true,
8633         /**
8634          * @event change
8635          * Fires just before the field blurs if the field value has changed.
8636          * @param {Roo.form.Field} this
8637          * @param {Mixed} newValue The new value
8638          * @param {Mixed} oldValue The original value
8639          */
8640         change : true,
8641         /**
8642          * @event invalid
8643          * Fires after the field has been marked as invalid.
8644          * @param {Roo.form.Field} this
8645          * @param {String} msg The validation message
8646          */
8647         invalid : true,
8648         /**
8649          * @event valid
8650          * Fires after the field has been validated with no errors.
8651          * @param {Roo.form.Field} this
8652          */
8653         valid : true,
8654          /**
8655          * @event keyup
8656          * Fires after the key up
8657          * @param {Roo.form.Field} this
8658          * @param {Roo.EventObject}  e The event Object
8659          */
8660         keyup : true
8661     });
8662 };
8663
8664 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8665      /**
8666      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8667       automatic validation (defaults to "keyup").
8668      */
8669     validationEvent : "keyup",
8670      /**
8671      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8672      */
8673     validateOnBlur : true,
8674     /**
8675      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8676      */
8677     validationDelay : 250,
8678      /**
8679      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8680      */
8681     focusClass : "x-form-focus",  // not needed???
8682     
8683        
8684     /**
8685      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8686      */
8687     invalidClass : "has-warning",
8688     
8689     /**
8690      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8691      */
8692     validClass : "has-success",
8693     
8694     /**
8695      * @cfg {Boolean} hasFeedback (true|false) default true
8696      */
8697     hasFeedback : true,
8698     
8699     /**
8700      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8701      */
8702     invalidFeedbackClass : "glyphicon-warning-sign",
8703     
8704     /**
8705      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8706      */
8707     validFeedbackClass : "glyphicon-ok",
8708     
8709     /**
8710      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8711      */
8712     selectOnFocus : false,
8713     
8714      /**
8715      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8716      */
8717     maskRe : null,
8718        /**
8719      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8720      */
8721     vtype : null,
8722     
8723       /**
8724      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8725      */
8726     disableKeyFilter : false,
8727     
8728        /**
8729      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8730      */
8731     disabled : false,
8732      /**
8733      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8734      */
8735     allowBlank : true,
8736     /**
8737      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8738      */
8739     blankText : "Please complete this mandatory field",
8740     
8741      /**
8742      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8743      */
8744     minLength : 0,
8745     /**
8746      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8747      */
8748     maxLength : Number.MAX_VALUE,
8749     /**
8750      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8751      */
8752     minLengthText : "The minimum length for this field is {0}",
8753     /**
8754      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8755      */
8756     maxLengthText : "The maximum length for this field is {0}",
8757   
8758     
8759     /**
8760      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8761      * If available, this function will be called only after the basic validators all return true, and will be passed the
8762      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8763      */
8764     validator : null,
8765     /**
8766      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8767      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8768      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8769      */
8770     regex : null,
8771     /**
8772      * @cfg {String} regexText -- Depricated - use Invalid Text
8773      */
8774     regexText : "",
8775     
8776     /**
8777      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8778      */
8779     invalidText : "",
8780     
8781     
8782     
8783     autocomplete: false,
8784     
8785     
8786     fieldLabel : '',
8787     inputType : 'text',
8788     
8789     name : false,
8790     placeholder: false,
8791     before : false,
8792     after : false,
8793     size : false,
8794     hasFocus : false,
8795     preventMark: false,
8796     isFormField : true,
8797     value : '',
8798     labelWidth : 2,
8799     labelAlign : false,
8800     readOnly : false,
8801     align : false,
8802     formatedValue : false,
8803     forceFeedback : false,
8804     
8805     indicatorpos : 'left',
8806     
8807     labellg : 0,
8808     labelmd : 0,
8809     labelsm : 0,
8810     labelxs : 0,
8811     
8812     capture : '',
8813     accept : '',
8814     
8815     parentLabelAlign : function()
8816     {
8817         var parent = this;
8818         while (parent.parent()) {
8819             parent = parent.parent();
8820             if (typeof(parent.labelAlign) !='undefined') {
8821                 return parent.labelAlign;
8822             }
8823         }
8824         return 'left';
8825         
8826     },
8827     
8828     getAutoCreate : function()
8829     {
8830         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8831         
8832         var id = Roo.id();
8833         
8834         var cfg = {};
8835         
8836         if(this.inputType != 'hidden'){
8837             cfg.cls = 'form-group' //input-group
8838         }
8839         
8840         var input =  {
8841             tag: 'input',
8842             id : id,
8843             type : this.inputType,
8844             value : this.value,
8845             cls : 'form-control',
8846             placeholder : this.placeholder || '',
8847             autocomplete : this.autocomplete || 'new-password'
8848         };
8849         
8850         if(this.capture.length){
8851             input.capture = this.capture;
8852         }
8853         
8854         if(this.accept.length){
8855             input.accept = this.accept + "/*";
8856         }
8857         
8858         if(this.align){
8859             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8860         }
8861         
8862         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8863             input.maxLength = this.maxLength;
8864         }
8865         
8866         if (this.disabled) {
8867             input.disabled=true;
8868         }
8869         
8870         if (this.readOnly) {
8871             input.readonly=true;
8872         }
8873         
8874         if (this.name) {
8875             input.name = this.name;
8876         }
8877         
8878         if (this.size) {
8879             input.cls += ' input-' + this.size;
8880         }
8881         
8882         var settings=this;
8883         ['xs','sm','md','lg'].map(function(size){
8884             if (settings[size]) {
8885                 cfg.cls += ' col-' + size + '-' + settings[size];
8886             }
8887         });
8888         
8889         var inputblock = input;
8890         
8891         var feedback = {
8892             tag: 'span',
8893             cls: 'glyphicon form-control-feedback'
8894         };
8895             
8896         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8897             
8898             inputblock = {
8899                 cls : 'has-feedback',
8900                 cn :  [
8901                     input,
8902                     feedback
8903                 ] 
8904             };  
8905         }
8906         
8907         if (this.before || this.after) {
8908             
8909             inputblock = {
8910                 cls : 'input-group',
8911                 cn :  [] 
8912             };
8913             
8914             if (this.before && typeof(this.before) == 'string') {
8915                 
8916                 inputblock.cn.push({
8917                     tag :'span',
8918                     cls : 'roo-input-before input-group-addon',
8919                     html : this.before
8920                 });
8921             }
8922             if (this.before && typeof(this.before) == 'object') {
8923                 this.before = Roo.factory(this.before);
8924                 
8925                 inputblock.cn.push({
8926                     tag :'span',
8927                     cls : 'roo-input-before input-group-' +
8928                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8929                 });
8930             }
8931             
8932             inputblock.cn.push(input);
8933             
8934             if (this.after && typeof(this.after) == 'string') {
8935                 inputblock.cn.push({
8936                     tag :'span',
8937                     cls : 'roo-input-after input-group-addon',
8938                     html : this.after
8939                 });
8940             }
8941             if (this.after && typeof(this.after) == 'object') {
8942                 this.after = Roo.factory(this.after);
8943                 
8944                 inputblock.cn.push({
8945                     tag :'span',
8946                     cls : 'roo-input-after input-group-' +
8947                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8948                 });
8949             }
8950             
8951             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8952                 inputblock.cls += ' has-feedback';
8953                 inputblock.cn.push(feedback);
8954             }
8955         };
8956         
8957         if (align ==='left' && this.fieldLabel.length) {
8958             
8959             cfg.cls += ' roo-form-group-label-left';
8960             
8961             cfg.cn = [
8962                 {
8963                     tag : 'i',
8964                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8965                     tooltip : 'This field is required'
8966                 },
8967                 {
8968                     tag: 'label',
8969                     'for' :  id,
8970                     cls : 'control-label',
8971                     html : this.fieldLabel
8972
8973                 },
8974                 {
8975                     cls : "", 
8976                     cn: [
8977                         inputblock
8978                     ]
8979                 }
8980             ];
8981             
8982             var labelCfg = cfg.cn[1];
8983             var contentCfg = cfg.cn[2];
8984             
8985             if(this.indicatorpos == 'right'){
8986                 cfg.cn = [
8987                     {
8988                         tag: 'label',
8989                         'for' :  id,
8990                         cls : 'control-label',
8991                         cn : [
8992                             {
8993                                 tag : 'span',
8994                                 html : this.fieldLabel
8995                             },
8996                             {
8997                                 tag : 'i',
8998                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8999                                 tooltip : 'This field is required'
9000                             }
9001                         ]
9002                     },
9003                     {
9004                         cls : "",
9005                         cn: [
9006                             inputblock
9007                         ]
9008                     }
9009
9010                 ];
9011                 
9012                 labelCfg = cfg.cn[0];
9013                 contentCfg = cfg.cn[1];
9014             
9015             }
9016             
9017             if(this.labelWidth > 12){
9018                 labelCfg.style = "width: " + this.labelWidth + 'px';
9019             }
9020             
9021             if(this.labelWidth < 13 && this.labelmd == 0){
9022                 this.labelmd = this.labelWidth;
9023             }
9024             
9025             if(this.labellg > 0){
9026                 labelCfg.cls += ' col-lg-' + this.labellg;
9027                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9028             }
9029             
9030             if(this.labelmd > 0){
9031                 labelCfg.cls += ' col-md-' + this.labelmd;
9032                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9033             }
9034             
9035             if(this.labelsm > 0){
9036                 labelCfg.cls += ' col-sm-' + this.labelsm;
9037                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9038             }
9039             
9040             if(this.labelxs > 0){
9041                 labelCfg.cls += ' col-xs-' + this.labelxs;
9042                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9043             }
9044             
9045             
9046         } else if ( this.fieldLabel.length) {
9047                 
9048             cfg.cn = [
9049                 {
9050                     tag : 'i',
9051                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9052                     tooltip : 'This field is required'
9053                 },
9054                 {
9055                     tag: 'label',
9056                    //cls : 'input-group-addon',
9057                     html : this.fieldLabel
9058
9059                 },
9060
9061                inputblock
9062
9063            ];
9064            
9065            if(this.indicatorpos == 'right'){
9066                 
9067                 cfg.cn = [
9068                     {
9069                         tag: 'label',
9070                        //cls : 'input-group-addon',
9071                         html : this.fieldLabel
9072
9073                     },
9074                     {
9075                         tag : 'i',
9076                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9077                         tooltip : 'This field is required'
9078                     },
9079
9080                    inputblock
9081
9082                ];
9083
9084             }
9085
9086         } else {
9087             
9088             cfg.cn = [
9089
9090                     inputblock
9091
9092             ];
9093                 
9094                 
9095         };
9096         
9097         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9098            cfg.cls += ' navbar-form';
9099         }
9100         
9101         if (this.parentType === 'NavGroup') {
9102            cfg.cls += ' navbar-form';
9103            cfg.tag = 'li';
9104         }
9105         
9106         return cfg;
9107         
9108     },
9109     /**
9110      * return the real input element.
9111      */
9112     inputEl: function ()
9113     {
9114         return this.el.select('input.form-control',true).first();
9115     },
9116     
9117     tooltipEl : function()
9118     {
9119         return this.inputEl();
9120     },
9121     
9122     indicatorEl : function()
9123     {
9124         var indicator = this.el.select('i.roo-required-indicator',true).first();
9125         
9126         if(!indicator){
9127             return false;
9128         }
9129         
9130         return indicator;
9131         
9132     },
9133     
9134     setDisabled : function(v)
9135     {
9136         var i  = this.inputEl().dom;
9137         if (!v) {
9138             i.removeAttribute('disabled');
9139             return;
9140             
9141         }
9142         i.setAttribute('disabled','true');
9143     },
9144     initEvents : function()
9145     {
9146           
9147         this.inputEl().on("keydown" , this.fireKey,  this);
9148         this.inputEl().on("focus", this.onFocus,  this);
9149         this.inputEl().on("blur", this.onBlur,  this);
9150         
9151         this.inputEl().relayEvent('keyup', this);
9152         
9153         this.indicator = this.indicatorEl();
9154         
9155         if(this.indicator){
9156             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9157         }
9158  
9159         // reference to original value for reset
9160         this.originalValue = this.getValue();
9161         //Roo.form.TextField.superclass.initEvents.call(this);
9162         if(this.validationEvent == 'keyup'){
9163             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9164             this.inputEl().on('keyup', this.filterValidation, this);
9165         }
9166         else if(this.validationEvent !== false){
9167             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9168         }
9169         
9170         if(this.selectOnFocus){
9171             this.on("focus", this.preFocus, this);
9172             
9173         }
9174         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9175             this.inputEl().on("keypress", this.filterKeys, this);
9176         } else {
9177             this.inputEl().relayEvent('keypress', this);
9178         }
9179        /* if(this.grow){
9180             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9181             this.el.on("click", this.autoSize,  this);
9182         }
9183         */
9184         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9185             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9186         }
9187         
9188         if (typeof(this.before) == 'object') {
9189             this.before.render(this.el.select('.roo-input-before',true).first());
9190         }
9191         if (typeof(this.after) == 'object') {
9192             this.after.render(this.el.select('.roo-input-after',true).first());
9193         }
9194         
9195         this.inputEl().on('change', this.onChange, this);
9196         
9197     },
9198     filterValidation : function(e){
9199         if(!e.isNavKeyPress()){
9200             this.validationTask.delay(this.validationDelay);
9201         }
9202     },
9203      /**
9204      * Validates the field value
9205      * @return {Boolean} True if the value is valid, else false
9206      */
9207     validate : function(){
9208         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9209         if(this.disabled || this.validateValue(this.getRawValue())){
9210             this.markValid();
9211             return true;
9212         }
9213         
9214         this.markInvalid();
9215         return false;
9216     },
9217     
9218     
9219     /**
9220      * Validates a value according to the field's validation rules and marks the field as invalid
9221      * if the validation fails
9222      * @param {Mixed} value The value to validate
9223      * @return {Boolean} True if the value is valid, else false
9224      */
9225     validateValue : function(value)
9226     {
9227         if(this.getVisibilityEl().hasClass('hidden')){
9228             return true;
9229         }
9230         
9231         if(value.length < 1)  { // if it's blank
9232             if(this.allowBlank){
9233                 return true;
9234             }
9235             return false;
9236         }
9237         
9238         if(value.length < this.minLength){
9239             return false;
9240         }
9241         if(value.length > this.maxLength){
9242             return false;
9243         }
9244         if(this.vtype){
9245             var vt = Roo.form.VTypes;
9246             if(!vt[this.vtype](value, this)){
9247                 return false;
9248             }
9249         }
9250         if(typeof this.validator == "function"){
9251             var msg = this.validator(value);
9252             if(msg !== true){
9253                 return false;
9254             }
9255             if (typeof(msg) == 'string') {
9256                 this.invalidText = msg;
9257             }
9258         }
9259         
9260         if(this.regex && !this.regex.test(value)){
9261             return false;
9262         }
9263         
9264         return true;
9265     },
9266     
9267      // private
9268     fireKey : function(e){
9269         //Roo.log('field ' + e.getKey());
9270         if(e.isNavKeyPress()){
9271             this.fireEvent("specialkey", this, e);
9272         }
9273     },
9274     focus : function (selectText){
9275         if(this.rendered){
9276             this.inputEl().focus();
9277             if(selectText === true){
9278                 this.inputEl().dom.select();
9279             }
9280         }
9281         return this;
9282     } ,
9283     
9284     onFocus : function(){
9285         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9286            // this.el.addClass(this.focusClass);
9287         }
9288         if(!this.hasFocus){
9289             this.hasFocus = true;
9290             this.startValue = this.getValue();
9291             this.fireEvent("focus", this);
9292         }
9293     },
9294     
9295     beforeBlur : Roo.emptyFn,
9296
9297     
9298     // private
9299     onBlur : function(){
9300         this.beforeBlur();
9301         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9302             //this.el.removeClass(this.focusClass);
9303         }
9304         this.hasFocus = false;
9305         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9306             this.validate();
9307         }
9308         var v = this.getValue();
9309         if(String(v) !== String(this.startValue)){
9310             this.fireEvent('change', this, v, this.startValue);
9311         }
9312         this.fireEvent("blur", this);
9313     },
9314     
9315     onChange : function(e)
9316     {
9317         var v = this.getValue();
9318         if(String(v) !== String(this.startValue)){
9319             this.fireEvent('change', this, v, this.startValue);
9320         }
9321         
9322     },
9323     
9324     /**
9325      * Resets the current field value to the originally loaded value and clears any validation messages
9326      */
9327     reset : function(){
9328         this.setValue(this.originalValue);
9329         this.validate();
9330     },
9331      /**
9332      * Returns the name of the field
9333      * @return {Mixed} name The name field
9334      */
9335     getName: function(){
9336         return this.name;
9337     },
9338      /**
9339      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9340      * @return {Mixed} value The field value
9341      */
9342     getValue : function(){
9343         
9344         var v = this.inputEl().getValue();
9345         
9346         return v;
9347     },
9348     /**
9349      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9350      * @return {Mixed} value The field value
9351      */
9352     getRawValue : function(){
9353         var v = this.inputEl().getValue();
9354         
9355         return v;
9356     },
9357     
9358     /**
9359      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9360      * @param {Mixed} value The value to set
9361      */
9362     setRawValue : function(v){
9363         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9364     },
9365     
9366     selectText : function(start, end){
9367         var v = this.getRawValue();
9368         if(v.length > 0){
9369             start = start === undefined ? 0 : start;
9370             end = end === undefined ? v.length : end;
9371             var d = this.inputEl().dom;
9372             if(d.setSelectionRange){
9373                 d.setSelectionRange(start, end);
9374             }else if(d.createTextRange){
9375                 var range = d.createTextRange();
9376                 range.moveStart("character", start);
9377                 range.moveEnd("character", v.length-end);
9378                 range.select();
9379             }
9380         }
9381     },
9382     
9383     /**
9384      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9385      * @param {Mixed} value The value to set
9386      */
9387     setValue : function(v){
9388         this.value = v;
9389         if(this.rendered){
9390             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9391             this.validate();
9392         }
9393     },
9394     
9395     /*
9396     processValue : function(value){
9397         if(this.stripCharsRe){
9398             var newValue = value.replace(this.stripCharsRe, '');
9399             if(newValue !== value){
9400                 this.setRawValue(newValue);
9401                 return newValue;
9402             }
9403         }
9404         return value;
9405     },
9406   */
9407     preFocus : function(){
9408         
9409         if(this.selectOnFocus){
9410             this.inputEl().dom.select();
9411         }
9412     },
9413     filterKeys : function(e){
9414         var k = e.getKey();
9415         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9416             return;
9417         }
9418         var c = e.getCharCode(), cc = String.fromCharCode(c);
9419         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9420             return;
9421         }
9422         if(!this.maskRe.test(cc)){
9423             e.stopEvent();
9424         }
9425     },
9426      /**
9427      * Clear any invalid styles/messages for this field
9428      */
9429     clearInvalid : function(){
9430         
9431         if(!this.el || this.preventMark){ // not rendered
9432             return;
9433         }
9434         
9435      
9436         this.el.removeClass(this.invalidClass);
9437         
9438         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9439             
9440             var feedback = this.el.select('.form-control-feedback', true).first();
9441             
9442             if(feedback){
9443                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9444             }
9445             
9446         }
9447         
9448         if(this.indicator){
9449             this.indicator.removeClass('visible');
9450             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9451         }
9452         
9453         this.fireEvent('valid', this);
9454     },
9455     
9456      /**
9457      * Mark this field as valid
9458      */
9459     markValid : function()
9460     {
9461         if(!this.el  || this.preventMark){ // not rendered...
9462             return;
9463         }
9464         
9465         this.el.removeClass([this.invalidClass, this.validClass]);
9466         
9467         var feedback = this.el.select('.form-control-feedback', true).first();
9468             
9469         if(feedback){
9470             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9471         }
9472         
9473         if(this.indicator){
9474             this.indicator.removeClass('visible');
9475             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9476         }
9477         
9478         if(this.disabled){
9479             return;
9480         }
9481         
9482         if(this.allowBlank && !this.getRawValue().length){
9483             return;
9484         }
9485         
9486         this.el.addClass(this.validClass);
9487         
9488         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9489             
9490             var feedback = this.el.select('.form-control-feedback', true).first();
9491             
9492             if(feedback){
9493                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9494                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9495             }
9496             
9497         }
9498         
9499         this.fireEvent('valid', this);
9500     },
9501     
9502      /**
9503      * Mark this field as invalid
9504      * @param {String} msg The validation message
9505      */
9506     markInvalid : function(msg)
9507     {
9508         if(!this.el  || this.preventMark){ // not rendered
9509             return;
9510         }
9511         
9512         this.el.removeClass([this.invalidClass, this.validClass]);
9513         
9514         var feedback = this.el.select('.form-control-feedback', true).first();
9515             
9516         if(feedback){
9517             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9518         }
9519
9520         if(this.disabled){
9521             return;
9522         }
9523         
9524         if(this.allowBlank && !this.getRawValue().length){
9525             return;
9526         }
9527         
9528         if(this.indicator){
9529             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9530             this.indicator.addClass('visible');
9531         }
9532         
9533         this.el.addClass(this.invalidClass);
9534         
9535         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9536             
9537             var feedback = this.el.select('.form-control-feedback', true).first();
9538             
9539             if(feedback){
9540                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9541                 
9542                 if(this.getValue().length || this.forceFeedback){
9543                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9544                 }
9545                 
9546             }
9547             
9548         }
9549         
9550         this.fireEvent('invalid', this, msg);
9551     },
9552     // private
9553     SafariOnKeyDown : function(event)
9554     {
9555         // this is a workaround for a password hang bug on chrome/ webkit.
9556         if (this.inputEl().dom.type != 'password') {
9557             return;
9558         }
9559         
9560         var isSelectAll = false;
9561         
9562         if(this.inputEl().dom.selectionEnd > 0){
9563             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9564         }
9565         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9566             event.preventDefault();
9567             this.setValue('');
9568             return;
9569         }
9570         
9571         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9572             
9573             event.preventDefault();
9574             // this is very hacky as keydown always get's upper case.
9575             //
9576             var cc = String.fromCharCode(event.getCharCode());
9577             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9578             
9579         }
9580     },
9581     adjustWidth : function(tag, w){
9582         tag = tag.toLowerCase();
9583         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9584             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9585                 if(tag == 'input'){
9586                     return w + 2;
9587                 }
9588                 if(tag == 'textarea'){
9589                     return w-2;
9590                 }
9591             }else if(Roo.isOpera){
9592                 if(tag == 'input'){
9593                     return w + 2;
9594                 }
9595                 if(tag == 'textarea'){
9596                     return w-2;
9597                 }
9598             }
9599         }
9600         return w;
9601     },
9602     
9603     setFieldLabel : function(v)
9604     {
9605         if(!this.rendered){
9606             return;
9607         }
9608         
9609         if(this.indicator){
9610             var ar = this.el.select('label > span',true);
9611             
9612             if (ar.elements.length) {
9613                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9614                 this.fieldLabel = v;
9615                 return;
9616             }
9617             
9618             var br = this.el.select('label',true);
9619             
9620             if(br.elements.length) {
9621                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9622                 this.fieldLabel = v;
9623                 return;
9624             }
9625             
9626             Roo.log('Cannot Found any of label > span || label in input');
9627             return;
9628         }
9629         
9630         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9631         this.fieldLabel = v;
9632         
9633         
9634     }
9635 });
9636
9637  
9638 /*
9639  * - LGPL
9640  *
9641  * Input
9642  * 
9643  */
9644
9645 /**
9646  * @class Roo.bootstrap.TextArea
9647  * @extends Roo.bootstrap.Input
9648  * Bootstrap TextArea class
9649  * @cfg {Number} cols Specifies the visible width of a text area
9650  * @cfg {Number} rows Specifies the visible number of lines in a text area
9651  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9652  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9653  * @cfg {string} html text
9654  * 
9655  * @constructor
9656  * Create a new TextArea
9657  * @param {Object} config The config object
9658  */
9659
9660 Roo.bootstrap.TextArea = function(config){
9661     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9662    
9663 };
9664
9665 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9666      
9667     cols : false,
9668     rows : 5,
9669     readOnly : false,
9670     warp : 'soft',
9671     resize : false,
9672     value: false,
9673     html: false,
9674     
9675     getAutoCreate : function(){
9676         
9677         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9678         
9679         var id = Roo.id();
9680         
9681         var cfg = {};
9682         
9683         if(this.inputType != 'hidden'){
9684             cfg.cls = 'form-group' //input-group
9685         }
9686         
9687         var input =  {
9688             tag: 'textarea',
9689             id : id,
9690             warp : this.warp,
9691             rows : this.rows,
9692             value : this.value || '',
9693             html: this.html || '',
9694             cls : 'form-control',
9695             placeholder : this.placeholder || '' 
9696             
9697         };
9698         
9699         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9700             input.maxLength = this.maxLength;
9701         }
9702         
9703         if(this.resize){
9704             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9705         }
9706         
9707         if(this.cols){
9708             input.cols = this.cols;
9709         }
9710         
9711         if (this.readOnly) {
9712             input.readonly = true;
9713         }
9714         
9715         if (this.name) {
9716             input.name = this.name;
9717         }
9718         
9719         if (this.size) {
9720             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9721         }
9722         
9723         var settings=this;
9724         ['xs','sm','md','lg'].map(function(size){
9725             if (settings[size]) {
9726                 cfg.cls += ' col-' + size + '-' + settings[size];
9727             }
9728         });
9729         
9730         var inputblock = input;
9731         
9732         if(this.hasFeedback && !this.allowBlank){
9733             
9734             var feedback = {
9735                 tag: 'span',
9736                 cls: 'glyphicon form-control-feedback'
9737             };
9738
9739             inputblock = {
9740                 cls : 'has-feedback',
9741                 cn :  [
9742                     input,
9743                     feedback
9744                 ] 
9745             };  
9746         }
9747         
9748         
9749         if (this.before || this.after) {
9750             
9751             inputblock = {
9752                 cls : 'input-group',
9753                 cn :  [] 
9754             };
9755             if (this.before) {
9756                 inputblock.cn.push({
9757                     tag :'span',
9758                     cls : 'input-group-addon',
9759                     html : this.before
9760                 });
9761             }
9762             
9763             inputblock.cn.push(input);
9764             
9765             if(this.hasFeedback && !this.allowBlank){
9766                 inputblock.cls += ' has-feedback';
9767                 inputblock.cn.push(feedback);
9768             }
9769             
9770             if (this.after) {
9771                 inputblock.cn.push({
9772                     tag :'span',
9773                     cls : 'input-group-addon',
9774                     html : this.after
9775                 });
9776             }
9777             
9778         }
9779         
9780         if (align ==='left' && this.fieldLabel.length) {
9781             cfg.cn = [
9782                 {
9783                     tag: 'label',
9784                     'for' :  id,
9785                     cls : 'control-label',
9786                     html : this.fieldLabel
9787                 },
9788                 {
9789                     cls : "",
9790                     cn: [
9791                         inputblock
9792                     ]
9793                 }
9794
9795             ];
9796             
9797             if(this.labelWidth > 12){
9798                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9799             }
9800
9801             if(this.labelWidth < 13 && this.labelmd == 0){
9802                 this.labelmd = this.labelWidth;
9803             }
9804
9805             if(this.labellg > 0){
9806                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9807                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9808             }
9809
9810             if(this.labelmd > 0){
9811                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9812                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9813             }
9814
9815             if(this.labelsm > 0){
9816                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9817                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9818             }
9819
9820             if(this.labelxs > 0){
9821                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9822                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9823             }
9824             
9825         } else if ( this.fieldLabel.length) {
9826             cfg.cn = [
9827
9828                {
9829                    tag: 'label',
9830                    //cls : 'input-group-addon',
9831                    html : this.fieldLabel
9832
9833                },
9834
9835                inputblock
9836
9837            ];
9838
9839         } else {
9840
9841             cfg.cn = [
9842
9843                 inputblock
9844
9845             ];
9846                 
9847         }
9848         
9849         if (this.disabled) {
9850             input.disabled=true;
9851         }
9852         
9853         return cfg;
9854         
9855     },
9856     /**
9857      * return the real textarea element.
9858      */
9859     inputEl: function ()
9860     {
9861         return this.el.select('textarea.form-control',true).first();
9862     },
9863     
9864     /**
9865      * Clear any invalid styles/messages for this field
9866      */
9867     clearInvalid : function()
9868     {
9869         
9870         if(!this.el || this.preventMark){ // not rendered
9871             return;
9872         }
9873         
9874         var label = this.el.select('label', true).first();
9875         var icon = this.el.select('i.fa-star', true).first();
9876         
9877         if(label && icon){
9878             icon.remove();
9879         }
9880         
9881         this.el.removeClass(this.invalidClass);
9882         
9883         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9884             
9885             var feedback = this.el.select('.form-control-feedback', true).first();
9886             
9887             if(feedback){
9888                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9889             }
9890             
9891         }
9892         
9893         this.fireEvent('valid', this);
9894     },
9895     
9896      /**
9897      * Mark this field as valid
9898      */
9899     markValid : function()
9900     {
9901         if(!this.el  || this.preventMark){ // not rendered
9902             return;
9903         }
9904         
9905         this.el.removeClass([this.invalidClass, this.validClass]);
9906         
9907         var feedback = this.el.select('.form-control-feedback', true).first();
9908             
9909         if(feedback){
9910             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9911         }
9912
9913         if(this.disabled || this.allowBlank){
9914             return;
9915         }
9916         
9917         var label = this.el.select('label', true).first();
9918         var icon = this.el.select('i.fa-star', true).first();
9919         
9920         if(label && icon){
9921             icon.remove();
9922         }
9923         
9924         this.el.addClass(this.validClass);
9925         
9926         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9927             
9928             var feedback = this.el.select('.form-control-feedback', true).first();
9929             
9930             if(feedback){
9931                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9932                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9933             }
9934             
9935         }
9936         
9937         this.fireEvent('valid', this);
9938     },
9939     
9940      /**
9941      * Mark this field as invalid
9942      * @param {String} msg The validation message
9943      */
9944     markInvalid : function(msg)
9945     {
9946         if(!this.el  || this.preventMark){ // not rendered
9947             return;
9948         }
9949         
9950         this.el.removeClass([this.invalidClass, this.validClass]);
9951         
9952         var feedback = this.el.select('.form-control-feedback', true).first();
9953             
9954         if(feedback){
9955             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9956         }
9957
9958         if(this.disabled || this.allowBlank){
9959             return;
9960         }
9961         
9962         var label = this.el.select('label', true).first();
9963         var icon = this.el.select('i.fa-star', true).first();
9964         
9965         if(!this.getValue().length && label && !icon){
9966             this.el.createChild({
9967                 tag : 'i',
9968                 cls : 'text-danger fa fa-lg fa-star',
9969                 tooltip : 'This field is required',
9970                 style : 'margin-right:5px;'
9971             }, label, true);
9972         }
9973
9974         this.el.addClass(this.invalidClass);
9975         
9976         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9977             
9978             var feedback = this.el.select('.form-control-feedback', true).first();
9979             
9980             if(feedback){
9981                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9982                 
9983                 if(this.getValue().length || this.forceFeedback){
9984                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9985                 }
9986                 
9987             }
9988             
9989         }
9990         
9991         this.fireEvent('invalid', this, msg);
9992     }
9993 });
9994
9995  
9996 /*
9997  * - LGPL
9998  *
9999  * trigger field - base class for combo..
10000  * 
10001  */
10002  
10003 /**
10004  * @class Roo.bootstrap.TriggerField
10005  * @extends Roo.bootstrap.Input
10006  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10007  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10008  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10009  * for which you can provide a custom implementation.  For example:
10010  * <pre><code>
10011 var trigger = new Roo.bootstrap.TriggerField();
10012 trigger.onTriggerClick = myTriggerFn;
10013 trigger.applyTo('my-field');
10014 </code></pre>
10015  *
10016  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10017  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10018  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10019  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10020  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10021
10022  * @constructor
10023  * Create a new TriggerField.
10024  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10025  * to the base TextField)
10026  */
10027 Roo.bootstrap.TriggerField = function(config){
10028     this.mimicing = false;
10029     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10030 };
10031
10032 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10033     /**
10034      * @cfg {String} triggerClass A CSS class to apply to the trigger
10035      */
10036      /**
10037      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10038      */
10039     hideTrigger:false,
10040
10041     /**
10042      * @cfg {Boolean} removable (true|false) special filter default false
10043      */
10044     removable : false,
10045     
10046     /** @cfg {Boolean} grow @hide */
10047     /** @cfg {Number} growMin @hide */
10048     /** @cfg {Number} growMax @hide */
10049
10050     /**
10051      * @hide 
10052      * @method
10053      */
10054     autoSize: Roo.emptyFn,
10055     // private
10056     monitorTab : true,
10057     // private
10058     deferHeight : true,
10059
10060     
10061     actionMode : 'wrap',
10062     
10063     caret : false,
10064     
10065     
10066     getAutoCreate : function(){
10067        
10068         var align = this.labelAlign || this.parentLabelAlign();
10069         
10070         var id = Roo.id();
10071         
10072         var cfg = {
10073             cls: 'form-group' //input-group
10074         };
10075         
10076         
10077         var input =  {
10078             tag: 'input',
10079             id : id,
10080             type : this.inputType,
10081             cls : 'form-control',
10082             autocomplete: 'new-password',
10083             placeholder : this.placeholder || '' 
10084             
10085         };
10086         if (this.name) {
10087             input.name = this.name;
10088         }
10089         if (this.size) {
10090             input.cls += ' input-' + this.size;
10091         }
10092         
10093         if (this.disabled) {
10094             input.disabled=true;
10095         }
10096         
10097         var inputblock = input;
10098         
10099         if(this.hasFeedback && !this.allowBlank){
10100             
10101             var feedback = {
10102                 tag: 'span',
10103                 cls: 'glyphicon form-control-feedback'
10104             };
10105             
10106             if(this.removable && !this.editable && !this.tickable){
10107                 inputblock = {
10108                     cls : 'has-feedback',
10109                     cn :  [
10110                         inputblock,
10111                         {
10112                             tag: 'button',
10113                             html : 'x',
10114                             cls : 'roo-combo-removable-btn close'
10115                         },
10116                         feedback
10117                     ] 
10118                 };
10119             } else {
10120                 inputblock = {
10121                     cls : 'has-feedback',
10122                     cn :  [
10123                         inputblock,
10124                         feedback
10125                     ] 
10126                 };
10127             }
10128
10129         } else {
10130             if(this.removable && !this.editable && !this.tickable){
10131                 inputblock = {
10132                     cls : 'roo-removable',
10133                     cn :  [
10134                         inputblock,
10135                         {
10136                             tag: 'button',
10137                             html : 'x',
10138                             cls : 'roo-combo-removable-btn close'
10139                         }
10140                     ] 
10141                 };
10142             }
10143         }
10144         
10145         if (this.before || this.after) {
10146             
10147             inputblock = {
10148                 cls : 'input-group',
10149                 cn :  [] 
10150             };
10151             if (this.before) {
10152                 inputblock.cn.push({
10153                     tag :'span',
10154                     cls : 'input-group-addon',
10155                     html : this.before
10156                 });
10157             }
10158             
10159             inputblock.cn.push(input);
10160             
10161             if(this.hasFeedback && !this.allowBlank){
10162                 inputblock.cls += ' has-feedback';
10163                 inputblock.cn.push(feedback);
10164             }
10165             
10166             if (this.after) {
10167                 inputblock.cn.push({
10168                     tag :'span',
10169                     cls : 'input-group-addon',
10170                     html : this.after
10171                 });
10172             }
10173             
10174         };
10175         
10176         var box = {
10177             tag: 'div',
10178             cn: [
10179                 {
10180                     tag: 'input',
10181                     type : 'hidden',
10182                     cls: 'form-hidden-field'
10183                 },
10184                 inputblock
10185             ]
10186             
10187         };
10188         
10189         if(this.multiple){
10190             box = {
10191                 tag: 'div',
10192                 cn: [
10193                     {
10194                         tag: 'input',
10195                         type : 'hidden',
10196                         cls: 'form-hidden-field'
10197                     },
10198                     {
10199                         tag: 'ul',
10200                         cls: 'roo-select2-choices',
10201                         cn:[
10202                             {
10203                                 tag: 'li',
10204                                 cls: 'roo-select2-search-field',
10205                                 cn: [
10206
10207                                     inputblock
10208                                 ]
10209                             }
10210                         ]
10211                     }
10212                 ]
10213             }
10214         };
10215         
10216         var combobox = {
10217             cls: 'roo-select2-container input-group',
10218             cn: [
10219                 box
10220 //                {
10221 //                    tag: 'ul',
10222 //                    cls: 'typeahead typeahead-long dropdown-menu',
10223 //                    style: 'display:none'
10224 //                }
10225             ]
10226         };
10227         
10228         if(!this.multiple && this.showToggleBtn){
10229             
10230             var caret = {
10231                         tag: 'span',
10232                         cls: 'caret'
10233              };
10234             if (this.caret != false) {
10235                 caret = {
10236                      tag: 'i',
10237                      cls: 'fa fa-' + this.caret
10238                 };
10239                 
10240             }
10241             
10242             combobox.cn.push({
10243                 tag :'span',
10244                 cls : 'input-group-addon btn dropdown-toggle',
10245                 cn : [
10246                     caret,
10247                     {
10248                         tag: 'span',
10249                         cls: 'combobox-clear',
10250                         cn  : [
10251                             {
10252                                 tag : 'i',
10253                                 cls: 'icon-remove'
10254                             }
10255                         ]
10256                     }
10257                 ]
10258
10259             })
10260         }
10261         
10262         if(this.multiple){
10263             combobox.cls += ' roo-select2-container-multi';
10264         }
10265         
10266         if (align ==='left' && this.fieldLabel.length) {
10267             
10268             cfg.cls += ' roo-form-group-label-left';
10269
10270             cfg.cn = [
10271                 {
10272                     tag : 'i',
10273                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10274                     tooltip : 'This field is required'
10275                 },
10276                 {
10277                     tag: 'label',
10278                     'for' :  id,
10279                     cls : 'control-label',
10280                     html : this.fieldLabel
10281
10282                 },
10283                 {
10284                     cls : "", 
10285                     cn: [
10286                         combobox
10287                     ]
10288                 }
10289
10290             ];
10291             
10292             var labelCfg = cfg.cn[1];
10293             var contentCfg = cfg.cn[2];
10294             
10295             if(this.indicatorpos == 'right'){
10296                 cfg.cn = [
10297                     {
10298                         tag: 'label',
10299                         'for' :  id,
10300                         cls : 'control-label',
10301                         cn : [
10302                             {
10303                                 tag : 'span',
10304                                 html : this.fieldLabel
10305                             },
10306                             {
10307                                 tag : 'i',
10308                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10309                                 tooltip : 'This field is required'
10310                             }
10311                         ]
10312                     },
10313                     {
10314                         cls : "", 
10315                         cn: [
10316                             combobox
10317                         ]
10318                     }
10319
10320                 ];
10321                 
10322                 labelCfg = cfg.cn[0];
10323                 contentCfg = cfg.cn[1];
10324             }
10325             
10326             if(this.labelWidth > 12){
10327                 labelCfg.style = "width: " + this.labelWidth + 'px';
10328             }
10329             
10330             if(this.labelWidth < 13 && this.labelmd == 0){
10331                 this.labelmd = this.labelWidth;
10332             }
10333             
10334             if(this.labellg > 0){
10335                 labelCfg.cls += ' col-lg-' + this.labellg;
10336                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10337             }
10338             
10339             if(this.labelmd > 0){
10340                 labelCfg.cls += ' col-md-' + this.labelmd;
10341                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10342             }
10343             
10344             if(this.labelsm > 0){
10345                 labelCfg.cls += ' col-sm-' + this.labelsm;
10346                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10347             }
10348             
10349             if(this.labelxs > 0){
10350                 labelCfg.cls += ' col-xs-' + this.labelxs;
10351                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10352             }
10353             
10354         } else if ( this.fieldLabel.length) {
10355 //                Roo.log(" label");
10356             cfg.cn = [
10357                 {
10358                    tag : 'i',
10359                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10360                    tooltip : 'This field is required'
10361                },
10362                {
10363                    tag: 'label',
10364                    //cls : 'input-group-addon',
10365                    html : this.fieldLabel
10366
10367                },
10368
10369                combobox
10370
10371             ];
10372             
10373             if(this.indicatorpos == 'right'){
10374                 
10375                 cfg.cn = [
10376                     {
10377                        tag: 'label',
10378                        cn : [
10379                            {
10380                                tag : 'span',
10381                                html : this.fieldLabel
10382                            },
10383                            {
10384                               tag : 'i',
10385                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10386                               tooltip : 'This field is required'
10387                            }
10388                        ]
10389
10390                     },
10391                     combobox
10392
10393                 ];
10394
10395             }
10396
10397         } else {
10398             
10399 //                Roo.log(" no label && no align");
10400                 cfg = combobox
10401                      
10402                 
10403         }
10404         
10405         var settings=this;
10406         ['xs','sm','md','lg'].map(function(size){
10407             if (settings[size]) {
10408                 cfg.cls += ' col-' + size + '-' + settings[size];
10409             }
10410         });
10411         
10412         return cfg;
10413         
10414     },
10415     
10416     
10417     
10418     // private
10419     onResize : function(w, h){
10420 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10421 //        if(typeof w == 'number'){
10422 //            var x = w - this.trigger.getWidth();
10423 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10424 //            this.trigger.setStyle('left', x+'px');
10425 //        }
10426     },
10427
10428     // private
10429     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10430
10431     // private
10432     getResizeEl : function(){
10433         return this.inputEl();
10434     },
10435
10436     // private
10437     getPositionEl : function(){
10438         return this.inputEl();
10439     },
10440
10441     // private
10442     alignErrorIcon : function(){
10443         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10444     },
10445
10446     // private
10447     initEvents : function(){
10448         
10449         this.createList();
10450         
10451         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10452         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10453         if(!this.multiple && this.showToggleBtn){
10454             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10455             if(this.hideTrigger){
10456                 this.trigger.setDisplayed(false);
10457             }
10458             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10459         }
10460         
10461         if(this.multiple){
10462             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10463         }
10464         
10465         if(this.removable && !this.editable && !this.tickable){
10466             var close = this.closeTriggerEl();
10467             
10468             if(close){
10469                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10470                 close.on('click', this.removeBtnClick, this, close);
10471             }
10472         }
10473         
10474         //this.trigger.addClassOnOver('x-form-trigger-over');
10475         //this.trigger.addClassOnClick('x-form-trigger-click');
10476         
10477         //if(!this.width){
10478         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10479         //}
10480     },
10481     
10482     closeTriggerEl : function()
10483     {
10484         var close = this.el.select('.roo-combo-removable-btn', true).first();
10485         return close ? close : false;
10486     },
10487     
10488     removeBtnClick : function(e, h, el)
10489     {
10490         e.preventDefault();
10491         
10492         if(this.fireEvent("remove", this) !== false){
10493             this.reset();
10494             this.fireEvent("afterremove", this)
10495         }
10496     },
10497     
10498     createList : function()
10499     {
10500         this.list = Roo.get(document.body).createChild({
10501             tag: 'ul',
10502             cls: 'typeahead typeahead-long dropdown-menu',
10503             style: 'display:none'
10504         });
10505         
10506         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10507         
10508     },
10509
10510     // private
10511     initTrigger : function(){
10512        
10513     },
10514
10515     // private
10516     onDestroy : function(){
10517         if(this.trigger){
10518             this.trigger.removeAllListeners();
10519           //  this.trigger.remove();
10520         }
10521         //if(this.wrap){
10522         //    this.wrap.remove();
10523         //}
10524         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10525     },
10526
10527     // private
10528     onFocus : function(){
10529         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10530         /*
10531         if(!this.mimicing){
10532             this.wrap.addClass('x-trigger-wrap-focus');
10533             this.mimicing = true;
10534             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10535             if(this.monitorTab){
10536                 this.el.on("keydown", this.checkTab, this);
10537             }
10538         }
10539         */
10540     },
10541
10542     // private
10543     checkTab : function(e){
10544         if(e.getKey() == e.TAB){
10545             this.triggerBlur();
10546         }
10547     },
10548
10549     // private
10550     onBlur : function(){
10551         // do nothing
10552     },
10553
10554     // private
10555     mimicBlur : function(e, t){
10556         /*
10557         if(!this.wrap.contains(t) && this.validateBlur()){
10558             this.triggerBlur();
10559         }
10560         */
10561     },
10562
10563     // private
10564     triggerBlur : function(){
10565         this.mimicing = false;
10566         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10567         if(this.monitorTab){
10568             this.el.un("keydown", this.checkTab, this);
10569         }
10570         //this.wrap.removeClass('x-trigger-wrap-focus');
10571         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10572     },
10573
10574     // private
10575     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10576     validateBlur : function(e, t){
10577         return true;
10578     },
10579
10580     // private
10581     onDisable : function(){
10582         this.inputEl().dom.disabled = true;
10583         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10584         //if(this.wrap){
10585         //    this.wrap.addClass('x-item-disabled');
10586         //}
10587     },
10588
10589     // private
10590     onEnable : function(){
10591         this.inputEl().dom.disabled = false;
10592         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10593         //if(this.wrap){
10594         //    this.el.removeClass('x-item-disabled');
10595         //}
10596     },
10597
10598     // private
10599     onShow : function(){
10600         var ae = this.getActionEl();
10601         
10602         if(ae){
10603             ae.dom.style.display = '';
10604             ae.dom.style.visibility = 'visible';
10605         }
10606     },
10607
10608     // private
10609     
10610     onHide : function(){
10611         var ae = this.getActionEl();
10612         ae.dom.style.display = 'none';
10613     },
10614
10615     /**
10616      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10617      * by an implementing function.
10618      * @method
10619      * @param {EventObject} e
10620      */
10621     onTriggerClick : Roo.emptyFn
10622 });
10623  /*
10624  * Based on:
10625  * Ext JS Library 1.1.1
10626  * Copyright(c) 2006-2007, Ext JS, LLC.
10627  *
10628  * Originally Released Under LGPL - original licence link has changed is not relivant.
10629  *
10630  * Fork - LGPL
10631  * <script type="text/javascript">
10632  */
10633
10634
10635 /**
10636  * @class Roo.data.SortTypes
10637  * @singleton
10638  * Defines the default sorting (casting?) comparison functions used when sorting data.
10639  */
10640 Roo.data.SortTypes = {
10641     /**
10642      * Default sort that does nothing
10643      * @param {Mixed} s The value being converted
10644      * @return {Mixed} The comparison value
10645      */
10646     none : function(s){
10647         return s;
10648     },
10649     
10650     /**
10651      * The regular expression used to strip tags
10652      * @type {RegExp}
10653      * @property
10654      */
10655     stripTagsRE : /<\/?[^>]+>/gi,
10656     
10657     /**
10658      * Strips all HTML tags to sort on text only
10659      * @param {Mixed} s The value being converted
10660      * @return {String} The comparison value
10661      */
10662     asText : function(s){
10663         return String(s).replace(this.stripTagsRE, "");
10664     },
10665     
10666     /**
10667      * Strips all HTML tags to sort on text only - Case insensitive
10668      * @param {Mixed} s The value being converted
10669      * @return {String} The comparison value
10670      */
10671     asUCText : function(s){
10672         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10673     },
10674     
10675     /**
10676      * Case insensitive string
10677      * @param {Mixed} s The value being converted
10678      * @return {String} The comparison value
10679      */
10680     asUCString : function(s) {
10681         return String(s).toUpperCase();
10682     },
10683     
10684     /**
10685      * Date sorting
10686      * @param {Mixed} s The value being converted
10687      * @return {Number} The comparison value
10688      */
10689     asDate : function(s) {
10690         if(!s){
10691             return 0;
10692         }
10693         if(s instanceof Date){
10694             return s.getTime();
10695         }
10696         return Date.parse(String(s));
10697     },
10698     
10699     /**
10700      * Float sorting
10701      * @param {Mixed} s The value being converted
10702      * @return {Float} The comparison value
10703      */
10704     asFloat : function(s) {
10705         var val = parseFloat(String(s).replace(/,/g, ""));
10706         if(isNaN(val)) {
10707             val = 0;
10708         }
10709         return val;
10710     },
10711     
10712     /**
10713      * Integer sorting
10714      * @param {Mixed} s The value being converted
10715      * @return {Number} The comparison value
10716      */
10717     asInt : function(s) {
10718         var val = parseInt(String(s).replace(/,/g, ""));
10719         if(isNaN(val)) {
10720             val = 0;
10721         }
10722         return val;
10723     }
10724 };/*
10725  * Based on:
10726  * Ext JS Library 1.1.1
10727  * Copyright(c) 2006-2007, Ext JS, LLC.
10728  *
10729  * Originally Released Under LGPL - original licence link has changed is not relivant.
10730  *
10731  * Fork - LGPL
10732  * <script type="text/javascript">
10733  */
10734
10735 /**
10736 * @class Roo.data.Record
10737  * Instances of this class encapsulate both record <em>definition</em> information, and record
10738  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10739  * to access Records cached in an {@link Roo.data.Store} object.<br>
10740  * <p>
10741  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10742  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10743  * objects.<br>
10744  * <p>
10745  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10746  * @constructor
10747  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10748  * {@link #create}. The parameters are the same.
10749  * @param {Array} data An associative Array of data values keyed by the field name.
10750  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10751  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10752  * not specified an integer id is generated.
10753  */
10754 Roo.data.Record = function(data, id){
10755     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10756     this.data = data;
10757 };
10758
10759 /**
10760  * Generate a constructor for a specific record layout.
10761  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10762  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10763  * Each field definition object may contain the following properties: <ul>
10764  * <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,
10765  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10766  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10767  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10768  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10769  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10770  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10771  * this may be omitted.</p></li>
10772  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10773  * <ul><li>auto (Default, implies no conversion)</li>
10774  * <li>string</li>
10775  * <li>int</li>
10776  * <li>float</li>
10777  * <li>boolean</li>
10778  * <li>date</li></ul></p></li>
10779  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10780  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10781  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10782  * by the Reader into an object that will be stored in the Record. It is passed the
10783  * following parameters:<ul>
10784  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10785  * </ul></p></li>
10786  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10787  * </ul>
10788  * <br>usage:<br><pre><code>
10789 var TopicRecord = Roo.data.Record.create(
10790     {name: 'title', mapping: 'topic_title'},
10791     {name: 'author', mapping: 'username'},
10792     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10793     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10794     {name: 'lastPoster', mapping: 'user2'},
10795     {name: 'excerpt', mapping: 'post_text'}
10796 );
10797
10798 var myNewRecord = new TopicRecord({
10799     title: 'Do my job please',
10800     author: 'noobie',
10801     totalPosts: 1,
10802     lastPost: new Date(),
10803     lastPoster: 'Animal',
10804     excerpt: 'No way dude!'
10805 });
10806 myStore.add(myNewRecord);
10807 </code></pre>
10808  * @method create
10809  * @static
10810  */
10811 Roo.data.Record.create = function(o){
10812     var f = function(){
10813         f.superclass.constructor.apply(this, arguments);
10814     };
10815     Roo.extend(f, Roo.data.Record);
10816     var p = f.prototype;
10817     p.fields = new Roo.util.MixedCollection(false, function(field){
10818         return field.name;
10819     });
10820     for(var i = 0, len = o.length; i < len; i++){
10821         p.fields.add(new Roo.data.Field(o[i]));
10822     }
10823     f.getField = function(name){
10824         return p.fields.get(name);  
10825     };
10826     return f;
10827 };
10828
10829 Roo.data.Record.AUTO_ID = 1000;
10830 Roo.data.Record.EDIT = 'edit';
10831 Roo.data.Record.REJECT = 'reject';
10832 Roo.data.Record.COMMIT = 'commit';
10833
10834 Roo.data.Record.prototype = {
10835     /**
10836      * Readonly flag - true if this record has been modified.
10837      * @type Boolean
10838      */
10839     dirty : false,
10840     editing : false,
10841     error: null,
10842     modified: null,
10843
10844     // private
10845     join : function(store){
10846         this.store = store;
10847     },
10848
10849     /**
10850      * Set the named field to the specified value.
10851      * @param {String} name The name of the field to set.
10852      * @param {Object} value The value to set the field to.
10853      */
10854     set : function(name, value){
10855         if(this.data[name] == value){
10856             return;
10857         }
10858         this.dirty = true;
10859         if(!this.modified){
10860             this.modified = {};
10861         }
10862         if(typeof this.modified[name] == 'undefined'){
10863             this.modified[name] = this.data[name];
10864         }
10865         this.data[name] = value;
10866         if(!this.editing && this.store){
10867             this.store.afterEdit(this);
10868         }       
10869     },
10870
10871     /**
10872      * Get the value of the named field.
10873      * @param {String} name The name of the field to get the value of.
10874      * @return {Object} The value of the field.
10875      */
10876     get : function(name){
10877         return this.data[name]; 
10878     },
10879
10880     // private
10881     beginEdit : function(){
10882         this.editing = true;
10883         this.modified = {}; 
10884     },
10885
10886     // private
10887     cancelEdit : function(){
10888         this.editing = false;
10889         delete this.modified;
10890     },
10891
10892     // private
10893     endEdit : function(){
10894         this.editing = false;
10895         if(this.dirty && this.store){
10896             this.store.afterEdit(this);
10897         }
10898     },
10899
10900     /**
10901      * Usually called by the {@link Roo.data.Store} which owns the Record.
10902      * Rejects all changes made to the Record since either creation, or the last commit operation.
10903      * Modified fields are reverted to their original values.
10904      * <p>
10905      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10906      * of reject operations.
10907      */
10908     reject : function(){
10909         var m = this.modified;
10910         for(var n in m){
10911             if(typeof m[n] != "function"){
10912                 this.data[n] = m[n];
10913             }
10914         }
10915         this.dirty = false;
10916         delete this.modified;
10917         this.editing = false;
10918         if(this.store){
10919             this.store.afterReject(this);
10920         }
10921     },
10922
10923     /**
10924      * Usually called by the {@link Roo.data.Store} which owns the Record.
10925      * Commits all changes made to the Record since either creation, or the last commit operation.
10926      * <p>
10927      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10928      * of commit operations.
10929      */
10930     commit : function(){
10931         this.dirty = false;
10932         delete this.modified;
10933         this.editing = false;
10934         if(this.store){
10935             this.store.afterCommit(this);
10936         }
10937     },
10938
10939     // private
10940     hasError : function(){
10941         return this.error != null;
10942     },
10943
10944     // private
10945     clearError : function(){
10946         this.error = null;
10947     },
10948
10949     /**
10950      * Creates a copy of this record.
10951      * @param {String} id (optional) A new record id if you don't want to use this record's id
10952      * @return {Record}
10953      */
10954     copy : function(newId) {
10955         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10956     }
10957 };/*
10958  * Based on:
10959  * Ext JS Library 1.1.1
10960  * Copyright(c) 2006-2007, Ext JS, LLC.
10961  *
10962  * Originally Released Under LGPL - original licence link has changed is not relivant.
10963  *
10964  * Fork - LGPL
10965  * <script type="text/javascript">
10966  */
10967
10968
10969
10970 /**
10971  * @class Roo.data.Store
10972  * @extends Roo.util.Observable
10973  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10974  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10975  * <p>
10976  * 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
10977  * has no knowledge of the format of the data returned by the Proxy.<br>
10978  * <p>
10979  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10980  * instances from the data object. These records are cached and made available through accessor functions.
10981  * @constructor
10982  * Creates a new Store.
10983  * @param {Object} config A config object containing the objects needed for the Store to access data,
10984  * and read the data into Records.
10985  */
10986 Roo.data.Store = function(config){
10987     this.data = new Roo.util.MixedCollection(false);
10988     this.data.getKey = function(o){
10989         return o.id;
10990     };
10991     this.baseParams = {};
10992     // private
10993     this.paramNames = {
10994         "start" : "start",
10995         "limit" : "limit",
10996         "sort" : "sort",
10997         "dir" : "dir",
10998         "multisort" : "_multisort"
10999     };
11000
11001     if(config && config.data){
11002         this.inlineData = config.data;
11003         delete config.data;
11004     }
11005
11006     Roo.apply(this, config);
11007     
11008     if(this.reader){ // reader passed
11009         this.reader = Roo.factory(this.reader, Roo.data);
11010         this.reader.xmodule = this.xmodule || false;
11011         if(!this.recordType){
11012             this.recordType = this.reader.recordType;
11013         }
11014         if(this.reader.onMetaChange){
11015             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11016         }
11017     }
11018
11019     if(this.recordType){
11020         this.fields = this.recordType.prototype.fields;
11021     }
11022     this.modified = [];
11023
11024     this.addEvents({
11025         /**
11026          * @event datachanged
11027          * Fires when the data cache has changed, and a widget which is using this Store
11028          * as a Record cache should refresh its view.
11029          * @param {Store} this
11030          */
11031         datachanged : true,
11032         /**
11033          * @event metachange
11034          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11035          * @param {Store} this
11036          * @param {Object} meta The JSON metadata
11037          */
11038         metachange : true,
11039         /**
11040          * @event add
11041          * Fires when Records have been added to the Store
11042          * @param {Store} this
11043          * @param {Roo.data.Record[]} records The array of Records added
11044          * @param {Number} index The index at which the record(s) were added
11045          */
11046         add : true,
11047         /**
11048          * @event remove
11049          * Fires when a Record has been removed from the Store
11050          * @param {Store} this
11051          * @param {Roo.data.Record} record The Record that was removed
11052          * @param {Number} index The index at which the record was removed
11053          */
11054         remove : true,
11055         /**
11056          * @event update
11057          * Fires when a Record has been updated
11058          * @param {Store} this
11059          * @param {Roo.data.Record} record The Record that was updated
11060          * @param {String} operation The update operation being performed.  Value may be one of:
11061          * <pre><code>
11062  Roo.data.Record.EDIT
11063  Roo.data.Record.REJECT
11064  Roo.data.Record.COMMIT
11065          * </code></pre>
11066          */
11067         update : true,
11068         /**
11069          * @event clear
11070          * Fires when the data cache has been cleared.
11071          * @param {Store} this
11072          */
11073         clear : true,
11074         /**
11075          * @event beforeload
11076          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11077          * the load action will be canceled.
11078          * @param {Store} this
11079          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11080          */
11081         beforeload : true,
11082         /**
11083          * @event beforeloadadd
11084          * Fires after a new set of Records has been loaded.
11085          * @param {Store} this
11086          * @param {Roo.data.Record[]} records The Records that were loaded
11087          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11088          */
11089         beforeloadadd : true,
11090         /**
11091          * @event load
11092          * Fires after a new set of Records has been loaded, before they are added to the store.
11093          * @param {Store} this
11094          * @param {Roo.data.Record[]} records The Records that were loaded
11095          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11096          * @params {Object} return from reader
11097          */
11098         load : true,
11099         /**
11100          * @event loadexception
11101          * Fires if an exception occurs in the Proxy during loading.
11102          * Called with the signature of the Proxy's "loadexception" event.
11103          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11104          * 
11105          * @param {Proxy} 
11106          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11107          * @param {Object} load options 
11108          * @param {Object} jsonData from your request (normally this contains the Exception)
11109          */
11110         loadexception : true
11111     });
11112     
11113     if(this.proxy){
11114         this.proxy = Roo.factory(this.proxy, Roo.data);
11115         this.proxy.xmodule = this.xmodule || false;
11116         this.relayEvents(this.proxy,  ["loadexception"]);
11117     }
11118     this.sortToggle = {};
11119     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11120
11121     Roo.data.Store.superclass.constructor.call(this);
11122
11123     if(this.inlineData){
11124         this.loadData(this.inlineData);
11125         delete this.inlineData;
11126     }
11127 };
11128
11129 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11130      /**
11131     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11132     * without a remote query - used by combo/forms at present.
11133     */
11134     
11135     /**
11136     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11137     */
11138     /**
11139     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11140     */
11141     /**
11142     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11143     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11144     */
11145     /**
11146     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11147     * on any HTTP request
11148     */
11149     /**
11150     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11151     */
11152     /**
11153     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11154     */
11155     multiSort: false,
11156     /**
11157     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11158     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11159     */
11160     remoteSort : false,
11161
11162     /**
11163     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11164      * loaded or when a record is removed. (defaults to false).
11165     */
11166     pruneModifiedRecords : false,
11167
11168     // private
11169     lastOptions : null,
11170
11171     /**
11172      * Add Records to the Store and fires the add event.
11173      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11174      */
11175     add : function(records){
11176         records = [].concat(records);
11177         for(var i = 0, len = records.length; i < len; i++){
11178             records[i].join(this);
11179         }
11180         var index = this.data.length;
11181         this.data.addAll(records);
11182         this.fireEvent("add", this, records, index);
11183     },
11184
11185     /**
11186      * Remove a Record from the Store and fires the remove event.
11187      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11188      */
11189     remove : function(record){
11190         var index = this.data.indexOf(record);
11191         this.data.removeAt(index);
11192  
11193         if(this.pruneModifiedRecords){
11194             this.modified.remove(record);
11195         }
11196         this.fireEvent("remove", this, record, index);
11197     },
11198
11199     /**
11200      * Remove all Records from the Store and fires the clear event.
11201      */
11202     removeAll : function(){
11203         this.data.clear();
11204         if(this.pruneModifiedRecords){
11205             this.modified = [];
11206         }
11207         this.fireEvent("clear", this);
11208     },
11209
11210     /**
11211      * Inserts Records to the Store at the given index and fires the add event.
11212      * @param {Number} index The start index at which to insert the passed Records.
11213      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11214      */
11215     insert : function(index, records){
11216         records = [].concat(records);
11217         for(var i = 0, len = records.length; i < len; i++){
11218             this.data.insert(index, records[i]);
11219             records[i].join(this);
11220         }
11221         this.fireEvent("add", this, records, index);
11222     },
11223
11224     /**
11225      * Get the index within the cache of the passed Record.
11226      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11227      * @return {Number} The index of the passed Record. Returns -1 if not found.
11228      */
11229     indexOf : function(record){
11230         return this.data.indexOf(record);
11231     },
11232
11233     /**
11234      * Get the index within the cache of the Record with the passed id.
11235      * @param {String} id The id of the Record to find.
11236      * @return {Number} The index of the Record. Returns -1 if not found.
11237      */
11238     indexOfId : function(id){
11239         return this.data.indexOfKey(id);
11240     },
11241
11242     /**
11243      * Get the Record with the specified id.
11244      * @param {String} id The id of the Record to find.
11245      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11246      */
11247     getById : function(id){
11248         return this.data.key(id);
11249     },
11250
11251     /**
11252      * Get the Record at the specified index.
11253      * @param {Number} index The index of the Record to find.
11254      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11255      */
11256     getAt : function(index){
11257         return this.data.itemAt(index);
11258     },
11259
11260     /**
11261      * Returns a range of Records between specified indices.
11262      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11263      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11264      * @return {Roo.data.Record[]} An array of Records
11265      */
11266     getRange : function(start, end){
11267         return this.data.getRange(start, end);
11268     },
11269
11270     // private
11271     storeOptions : function(o){
11272         o = Roo.apply({}, o);
11273         delete o.callback;
11274         delete o.scope;
11275         this.lastOptions = o;
11276     },
11277
11278     /**
11279      * Loads the Record cache from the configured Proxy using the configured Reader.
11280      * <p>
11281      * If using remote paging, then the first load call must specify the <em>start</em>
11282      * and <em>limit</em> properties in the options.params property to establish the initial
11283      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11284      * <p>
11285      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11286      * and this call will return before the new data has been loaded. Perform any post-processing
11287      * in a callback function, or in a "load" event handler.</strong>
11288      * <p>
11289      * @param {Object} options An object containing properties which control loading options:<ul>
11290      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11291      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11292      * passed the following arguments:<ul>
11293      * <li>r : Roo.data.Record[]</li>
11294      * <li>options: Options object from the load call</li>
11295      * <li>success: Boolean success indicator</li></ul></li>
11296      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11297      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11298      * </ul>
11299      */
11300     load : function(options){
11301         options = options || {};
11302         if(this.fireEvent("beforeload", this, options) !== false){
11303             this.storeOptions(options);
11304             var p = Roo.apply(options.params || {}, this.baseParams);
11305             // if meta was not loaded from remote source.. try requesting it.
11306             if (!this.reader.metaFromRemote) {
11307                 p._requestMeta = 1;
11308             }
11309             if(this.sortInfo && this.remoteSort){
11310                 var pn = this.paramNames;
11311                 p[pn["sort"]] = this.sortInfo.field;
11312                 p[pn["dir"]] = this.sortInfo.direction;
11313             }
11314             if (this.multiSort) {
11315                 var pn = this.paramNames;
11316                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11317             }
11318             
11319             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11320         }
11321     },
11322
11323     /**
11324      * Reloads the Record cache from the configured Proxy using the configured Reader and
11325      * the options from the last load operation performed.
11326      * @param {Object} options (optional) An object containing properties which may override the options
11327      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11328      * the most recently used options are reused).
11329      */
11330     reload : function(options){
11331         this.load(Roo.applyIf(options||{}, this.lastOptions));
11332     },
11333
11334     // private
11335     // Called as a callback by the Reader during a load operation.
11336     loadRecords : function(o, options, success){
11337         if(!o || success === false){
11338             if(success !== false){
11339                 this.fireEvent("load", this, [], options, o);
11340             }
11341             if(options.callback){
11342                 options.callback.call(options.scope || this, [], options, false);
11343             }
11344             return;
11345         }
11346         // if data returned failure - throw an exception.
11347         if (o.success === false) {
11348             // show a message if no listener is registered.
11349             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11350                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11351             }
11352             // loadmask wil be hooked into this..
11353             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11354             return;
11355         }
11356         var r = o.records, t = o.totalRecords || r.length;
11357         
11358         this.fireEvent("beforeloadadd", this, r, options, o);
11359         
11360         if(!options || options.add !== true){
11361             if(this.pruneModifiedRecords){
11362                 this.modified = [];
11363             }
11364             for(var i = 0, len = r.length; i < len; i++){
11365                 r[i].join(this);
11366             }
11367             if(this.snapshot){
11368                 this.data = this.snapshot;
11369                 delete this.snapshot;
11370             }
11371             this.data.clear();
11372             this.data.addAll(r);
11373             this.totalLength = t;
11374             this.applySort();
11375             this.fireEvent("datachanged", this);
11376         }else{
11377             this.totalLength = Math.max(t, this.data.length+r.length);
11378             this.add(r);
11379         }
11380         
11381         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11382                 
11383             var e = new Roo.data.Record({});
11384
11385             e.set(this.parent.displayField, this.parent.emptyTitle);
11386             e.set(this.parent.valueField, '');
11387
11388             this.insert(0, e);
11389         }
11390             
11391         this.fireEvent("load", this, r, options, o);
11392         if(options.callback){
11393             options.callback.call(options.scope || this, r, options, true);
11394         }
11395     },
11396
11397
11398     /**
11399      * Loads data from a passed data block. A Reader which understands the format of the data
11400      * must have been configured in the constructor.
11401      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11402      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11403      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11404      */
11405     loadData : function(o, append){
11406         var r = this.reader.readRecords(o);
11407         this.loadRecords(r, {add: append}, true);
11408     },
11409
11410     /**
11411      * Gets the number of cached records.
11412      * <p>
11413      * <em>If using paging, this may not be the total size of the dataset. If the data object
11414      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11415      * the data set size</em>
11416      */
11417     getCount : function(){
11418         return this.data.length || 0;
11419     },
11420
11421     /**
11422      * Gets the total number of records in the dataset as returned by the server.
11423      * <p>
11424      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11425      * the dataset size</em>
11426      */
11427     getTotalCount : function(){
11428         return this.totalLength || 0;
11429     },
11430
11431     /**
11432      * Returns the sort state of the Store as an object with two properties:
11433      * <pre><code>
11434  field {String} The name of the field by which the Records are sorted
11435  direction {String} The sort order, "ASC" or "DESC"
11436      * </code></pre>
11437      */
11438     getSortState : function(){
11439         return this.sortInfo;
11440     },
11441
11442     // private
11443     applySort : function(){
11444         if(this.sortInfo && !this.remoteSort){
11445             var s = this.sortInfo, f = s.field;
11446             var st = this.fields.get(f).sortType;
11447             var fn = function(r1, r2){
11448                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11449                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11450             };
11451             this.data.sort(s.direction, fn);
11452             if(this.snapshot && this.snapshot != this.data){
11453                 this.snapshot.sort(s.direction, fn);
11454             }
11455         }
11456     },
11457
11458     /**
11459      * Sets the default sort column and order to be used by the next load operation.
11460      * @param {String} fieldName The name of the field to sort by.
11461      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11462      */
11463     setDefaultSort : function(field, dir){
11464         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11465     },
11466
11467     /**
11468      * Sort the Records.
11469      * If remote sorting is used, the sort is performed on the server, and the cache is
11470      * reloaded. If local sorting is used, the cache is sorted internally.
11471      * @param {String} fieldName The name of the field to sort by.
11472      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11473      */
11474     sort : function(fieldName, dir){
11475         var f = this.fields.get(fieldName);
11476         if(!dir){
11477             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11478             
11479             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11480                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11481             }else{
11482                 dir = f.sortDir;
11483             }
11484         }
11485         this.sortToggle[f.name] = dir;
11486         this.sortInfo = {field: f.name, direction: dir};
11487         if(!this.remoteSort){
11488             this.applySort();
11489             this.fireEvent("datachanged", this);
11490         }else{
11491             this.load(this.lastOptions);
11492         }
11493     },
11494
11495     /**
11496      * Calls the specified function for each of the Records in the cache.
11497      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11498      * Returning <em>false</em> aborts and exits the iteration.
11499      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11500      */
11501     each : function(fn, scope){
11502         this.data.each(fn, scope);
11503     },
11504
11505     /**
11506      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11507      * (e.g., during paging).
11508      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11509      */
11510     getModifiedRecords : function(){
11511         return this.modified;
11512     },
11513
11514     // private
11515     createFilterFn : function(property, value, anyMatch){
11516         if(!value.exec){ // not a regex
11517             value = String(value);
11518             if(value.length == 0){
11519                 return false;
11520             }
11521             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11522         }
11523         return function(r){
11524             return value.test(r.data[property]);
11525         };
11526     },
11527
11528     /**
11529      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11530      * @param {String} property A field on your records
11531      * @param {Number} start The record index to start at (defaults to 0)
11532      * @param {Number} end The last record index to include (defaults to length - 1)
11533      * @return {Number} The sum
11534      */
11535     sum : function(property, start, end){
11536         var rs = this.data.items, v = 0;
11537         start = start || 0;
11538         end = (end || end === 0) ? end : rs.length-1;
11539
11540         for(var i = start; i <= end; i++){
11541             v += (rs[i].data[property] || 0);
11542         }
11543         return v;
11544     },
11545
11546     /**
11547      * Filter the records by a specified property.
11548      * @param {String} field A field on your records
11549      * @param {String/RegExp} value Either a string that the field
11550      * should start with or a RegExp to test against the field
11551      * @param {Boolean} anyMatch True to match any part not just the beginning
11552      */
11553     filter : function(property, value, anyMatch){
11554         var fn = this.createFilterFn(property, value, anyMatch);
11555         return fn ? this.filterBy(fn) : this.clearFilter();
11556     },
11557
11558     /**
11559      * Filter by a function. The specified function will be called with each
11560      * record in this data source. If the function returns true the record is included,
11561      * otherwise it is filtered.
11562      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11563      * @param {Object} scope (optional) The scope of the function (defaults to this)
11564      */
11565     filterBy : function(fn, scope){
11566         this.snapshot = this.snapshot || this.data;
11567         this.data = this.queryBy(fn, scope||this);
11568         this.fireEvent("datachanged", this);
11569     },
11570
11571     /**
11572      * Query the records by a specified property.
11573      * @param {String} field A field on your records
11574      * @param {String/RegExp} value Either a string that the field
11575      * should start with or a RegExp to test against the field
11576      * @param {Boolean} anyMatch True to match any part not just the beginning
11577      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11578      */
11579     query : function(property, value, anyMatch){
11580         var fn = this.createFilterFn(property, value, anyMatch);
11581         return fn ? this.queryBy(fn) : this.data.clone();
11582     },
11583
11584     /**
11585      * Query by a function. The specified function will be called with each
11586      * record in this data source. If the function returns true the record is included
11587      * in the results.
11588      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11589      * @param {Object} scope (optional) The scope of the function (defaults to this)
11590       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11591      **/
11592     queryBy : function(fn, scope){
11593         var data = this.snapshot || this.data;
11594         return data.filterBy(fn, scope||this);
11595     },
11596
11597     /**
11598      * Collects unique values for a particular dataIndex from this store.
11599      * @param {String} dataIndex The property to collect
11600      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11601      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11602      * @return {Array} An array of the unique values
11603      **/
11604     collect : function(dataIndex, allowNull, bypassFilter){
11605         var d = (bypassFilter === true && this.snapshot) ?
11606                 this.snapshot.items : this.data.items;
11607         var v, sv, r = [], l = {};
11608         for(var i = 0, len = d.length; i < len; i++){
11609             v = d[i].data[dataIndex];
11610             sv = String(v);
11611             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11612                 l[sv] = true;
11613                 r[r.length] = v;
11614             }
11615         }
11616         return r;
11617     },
11618
11619     /**
11620      * Revert to a view of the Record cache with no filtering applied.
11621      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11622      */
11623     clearFilter : function(suppressEvent){
11624         if(this.snapshot && this.snapshot != this.data){
11625             this.data = this.snapshot;
11626             delete this.snapshot;
11627             if(suppressEvent !== true){
11628                 this.fireEvent("datachanged", this);
11629             }
11630         }
11631     },
11632
11633     // private
11634     afterEdit : function(record){
11635         if(this.modified.indexOf(record) == -1){
11636             this.modified.push(record);
11637         }
11638         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11639     },
11640     
11641     // private
11642     afterReject : function(record){
11643         this.modified.remove(record);
11644         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11645     },
11646
11647     // private
11648     afterCommit : function(record){
11649         this.modified.remove(record);
11650         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11651     },
11652
11653     /**
11654      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11655      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11656      */
11657     commitChanges : function(){
11658         var m = this.modified.slice(0);
11659         this.modified = [];
11660         for(var i = 0, len = m.length; i < len; i++){
11661             m[i].commit();
11662         }
11663     },
11664
11665     /**
11666      * Cancel outstanding changes on all changed records.
11667      */
11668     rejectChanges : function(){
11669         var m = this.modified.slice(0);
11670         this.modified = [];
11671         for(var i = 0, len = m.length; i < len; i++){
11672             m[i].reject();
11673         }
11674     },
11675
11676     onMetaChange : function(meta, rtype, o){
11677         this.recordType = rtype;
11678         this.fields = rtype.prototype.fields;
11679         delete this.snapshot;
11680         this.sortInfo = meta.sortInfo || this.sortInfo;
11681         this.modified = [];
11682         this.fireEvent('metachange', this, this.reader.meta);
11683     },
11684     
11685     moveIndex : function(data, type)
11686     {
11687         var index = this.indexOf(data);
11688         
11689         var newIndex = index + type;
11690         
11691         this.remove(data);
11692         
11693         this.insert(newIndex, data);
11694         
11695     }
11696 });/*
11697  * Based on:
11698  * Ext JS Library 1.1.1
11699  * Copyright(c) 2006-2007, Ext JS, LLC.
11700  *
11701  * Originally Released Under LGPL - original licence link has changed is not relivant.
11702  *
11703  * Fork - LGPL
11704  * <script type="text/javascript">
11705  */
11706
11707 /**
11708  * @class Roo.data.SimpleStore
11709  * @extends Roo.data.Store
11710  * Small helper class to make creating Stores from Array data easier.
11711  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11712  * @cfg {Array} fields An array of field definition objects, or field name strings.
11713  * @cfg {Array} data The multi-dimensional array of data
11714  * @constructor
11715  * @param {Object} config
11716  */
11717 Roo.data.SimpleStore = function(config){
11718     Roo.data.SimpleStore.superclass.constructor.call(this, {
11719         isLocal : true,
11720         reader: new Roo.data.ArrayReader({
11721                 id: config.id
11722             },
11723             Roo.data.Record.create(config.fields)
11724         ),
11725         proxy : new Roo.data.MemoryProxy(config.data)
11726     });
11727     this.load();
11728 };
11729 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11730  * Based on:
11731  * Ext JS Library 1.1.1
11732  * Copyright(c) 2006-2007, Ext JS, LLC.
11733  *
11734  * Originally Released Under LGPL - original licence link has changed is not relivant.
11735  *
11736  * Fork - LGPL
11737  * <script type="text/javascript">
11738  */
11739
11740 /**
11741 /**
11742  * @extends Roo.data.Store
11743  * @class Roo.data.JsonStore
11744  * Small helper class to make creating Stores for JSON data easier. <br/>
11745 <pre><code>
11746 var store = new Roo.data.JsonStore({
11747     url: 'get-images.php',
11748     root: 'images',
11749     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11750 });
11751 </code></pre>
11752  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11753  * JsonReader and HttpProxy (unless inline data is provided).</b>
11754  * @cfg {Array} fields An array of field definition objects, or field name strings.
11755  * @constructor
11756  * @param {Object} config
11757  */
11758 Roo.data.JsonStore = function(c){
11759     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11760         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11761         reader: new Roo.data.JsonReader(c, c.fields)
11762     }));
11763 };
11764 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11765  * Based on:
11766  * Ext JS Library 1.1.1
11767  * Copyright(c) 2006-2007, Ext JS, LLC.
11768  *
11769  * Originally Released Under LGPL - original licence link has changed is not relivant.
11770  *
11771  * Fork - LGPL
11772  * <script type="text/javascript">
11773  */
11774
11775  
11776 Roo.data.Field = function(config){
11777     if(typeof config == "string"){
11778         config = {name: config};
11779     }
11780     Roo.apply(this, config);
11781     
11782     if(!this.type){
11783         this.type = "auto";
11784     }
11785     
11786     var st = Roo.data.SortTypes;
11787     // named sortTypes are supported, here we look them up
11788     if(typeof this.sortType == "string"){
11789         this.sortType = st[this.sortType];
11790     }
11791     
11792     // set default sortType for strings and dates
11793     if(!this.sortType){
11794         switch(this.type){
11795             case "string":
11796                 this.sortType = st.asUCString;
11797                 break;
11798             case "date":
11799                 this.sortType = st.asDate;
11800                 break;
11801             default:
11802                 this.sortType = st.none;
11803         }
11804     }
11805
11806     // define once
11807     var stripRe = /[\$,%]/g;
11808
11809     // prebuilt conversion function for this field, instead of
11810     // switching every time we're reading a value
11811     if(!this.convert){
11812         var cv, dateFormat = this.dateFormat;
11813         switch(this.type){
11814             case "":
11815             case "auto":
11816             case undefined:
11817                 cv = function(v){ return v; };
11818                 break;
11819             case "string":
11820                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11821                 break;
11822             case "int":
11823                 cv = function(v){
11824                     return v !== undefined && v !== null && v !== '' ?
11825                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11826                     };
11827                 break;
11828             case "float":
11829                 cv = function(v){
11830                     return v !== undefined && v !== null && v !== '' ?
11831                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11832                     };
11833                 break;
11834             case "bool":
11835             case "boolean":
11836                 cv = function(v){ return v === true || v === "true" || v == 1; };
11837                 break;
11838             case "date":
11839                 cv = function(v){
11840                     if(!v){
11841                         return '';
11842                     }
11843                     if(v instanceof Date){
11844                         return v;
11845                     }
11846                     if(dateFormat){
11847                         if(dateFormat == "timestamp"){
11848                             return new Date(v*1000);
11849                         }
11850                         return Date.parseDate(v, dateFormat);
11851                     }
11852                     var parsed = Date.parse(v);
11853                     return parsed ? new Date(parsed) : null;
11854                 };
11855              break;
11856             
11857         }
11858         this.convert = cv;
11859     }
11860 };
11861
11862 Roo.data.Field.prototype = {
11863     dateFormat: null,
11864     defaultValue: "",
11865     mapping: null,
11866     sortType : null,
11867     sortDir : "ASC"
11868 };/*
11869  * Based on:
11870  * Ext JS Library 1.1.1
11871  * Copyright(c) 2006-2007, Ext JS, LLC.
11872  *
11873  * Originally Released Under LGPL - original licence link has changed is not relivant.
11874  *
11875  * Fork - LGPL
11876  * <script type="text/javascript">
11877  */
11878  
11879 // Base class for reading structured data from a data source.  This class is intended to be
11880 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11881
11882 /**
11883  * @class Roo.data.DataReader
11884  * Base class for reading structured data from a data source.  This class is intended to be
11885  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11886  */
11887
11888 Roo.data.DataReader = function(meta, recordType){
11889     
11890     this.meta = meta;
11891     
11892     this.recordType = recordType instanceof Array ? 
11893         Roo.data.Record.create(recordType) : recordType;
11894 };
11895
11896 Roo.data.DataReader.prototype = {
11897      /**
11898      * Create an empty record
11899      * @param {Object} data (optional) - overlay some values
11900      * @return {Roo.data.Record} record created.
11901      */
11902     newRow :  function(d) {
11903         var da =  {};
11904         this.recordType.prototype.fields.each(function(c) {
11905             switch( c.type) {
11906                 case 'int' : da[c.name] = 0; break;
11907                 case 'date' : da[c.name] = new Date(); break;
11908                 case 'float' : da[c.name] = 0.0; break;
11909                 case 'boolean' : da[c.name] = false; break;
11910                 default : da[c.name] = ""; break;
11911             }
11912             
11913         });
11914         return new this.recordType(Roo.apply(da, d));
11915     }
11916     
11917 };/*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928 /**
11929  * @class Roo.data.DataProxy
11930  * @extends Roo.data.Observable
11931  * This class is an abstract base class for implementations which provide retrieval of
11932  * unformatted data objects.<br>
11933  * <p>
11934  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11935  * (of the appropriate type which knows how to parse the data object) to provide a block of
11936  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11937  * <p>
11938  * Custom implementations must implement the load method as described in
11939  * {@link Roo.data.HttpProxy#load}.
11940  */
11941 Roo.data.DataProxy = function(){
11942     this.addEvents({
11943         /**
11944          * @event beforeload
11945          * Fires before a network request is made to retrieve a data object.
11946          * @param {Object} This DataProxy object.
11947          * @param {Object} params The params parameter to the load function.
11948          */
11949         beforeload : true,
11950         /**
11951          * @event load
11952          * Fires before the load method's callback is called.
11953          * @param {Object} This DataProxy object.
11954          * @param {Object} o The data object.
11955          * @param {Object} arg The callback argument object passed to the load function.
11956          */
11957         load : true,
11958         /**
11959          * @event loadexception
11960          * Fires if an Exception occurs during data retrieval.
11961          * @param {Object} This DataProxy object.
11962          * @param {Object} o The data object.
11963          * @param {Object} arg The callback argument object passed to the load function.
11964          * @param {Object} e The Exception.
11965          */
11966         loadexception : true
11967     });
11968     Roo.data.DataProxy.superclass.constructor.call(this);
11969 };
11970
11971 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11972
11973     /**
11974      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11975      */
11976 /*
11977  * Based on:
11978  * Ext JS Library 1.1.1
11979  * Copyright(c) 2006-2007, Ext JS, LLC.
11980  *
11981  * Originally Released Under LGPL - original licence link has changed is not relivant.
11982  *
11983  * Fork - LGPL
11984  * <script type="text/javascript">
11985  */
11986 /**
11987  * @class Roo.data.MemoryProxy
11988  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11989  * to the Reader when its load method is called.
11990  * @constructor
11991  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11992  */
11993 Roo.data.MemoryProxy = function(data){
11994     if (data.data) {
11995         data = data.data;
11996     }
11997     Roo.data.MemoryProxy.superclass.constructor.call(this);
11998     this.data = data;
11999 };
12000
12001 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12002     
12003     /**
12004      * Load data from the requested source (in this case an in-memory
12005      * data object passed to the constructor), read the data object into
12006      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12007      * process that block using the passed callback.
12008      * @param {Object} params This parameter is not used by the MemoryProxy class.
12009      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12010      * object into a block of Roo.data.Records.
12011      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12012      * The function must be passed <ul>
12013      * <li>The Record block object</li>
12014      * <li>The "arg" argument from the load function</li>
12015      * <li>A boolean success indicator</li>
12016      * </ul>
12017      * @param {Object} scope The scope in which to call the callback
12018      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12019      */
12020     load : function(params, reader, callback, scope, arg){
12021         params = params || {};
12022         var result;
12023         try {
12024             result = reader.readRecords(this.data);
12025         }catch(e){
12026             this.fireEvent("loadexception", this, arg, null, e);
12027             callback.call(scope, null, arg, false);
12028             return;
12029         }
12030         callback.call(scope, result, arg, true);
12031     },
12032     
12033     // private
12034     update : function(params, records){
12035         
12036     }
12037 });/*
12038  * Based on:
12039  * Ext JS Library 1.1.1
12040  * Copyright(c) 2006-2007, Ext JS, LLC.
12041  *
12042  * Originally Released Under LGPL - original licence link has changed is not relivant.
12043  *
12044  * Fork - LGPL
12045  * <script type="text/javascript">
12046  */
12047 /**
12048  * @class Roo.data.HttpProxy
12049  * @extends Roo.data.DataProxy
12050  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12051  * configured to reference a certain URL.<br><br>
12052  * <p>
12053  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12054  * from which the running page was served.<br><br>
12055  * <p>
12056  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12057  * <p>
12058  * Be aware that to enable the browser to parse an XML document, the server must set
12059  * the Content-Type header in the HTTP response to "text/xml".
12060  * @constructor
12061  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12062  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12063  * will be used to make the request.
12064  */
12065 Roo.data.HttpProxy = function(conn){
12066     Roo.data.HttpProxy.superclass.constructor.call(this);
12067     // is conn a conn config or a real conn?
12068     this.conn = conn;
12069     this.useAjax = !conn || !conn.events;
12070   
12071 };
12072
12073 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12074     // thse are take from connection...
12075     
12076     /**
12077      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12078      */
12079     /**
12080      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12081      * extra parameters to each request made by this object. (defaults to undefined)
12082      */
12083     /**
12084      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12085      *  to each request made by this object. (defaults to undefined)
12086      */
12087     /**
12088      * @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)
12089      */
12090     /**
12091      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12092      */
12093      /**
12094      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12095      * @type Boolean
12096      */
12097   
12098
12099     /**
12100      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12101      * @type Boolean
12102      */
12103     /**
12104      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12105      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12106      * a finer-grained basis than the DataProxy events.
12107      */
12108     getConnection : function(){
12109         return this.useAjax ? Roo.Ajax : this.conn;
12110     },
12111
12112     /**
12113      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12114      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12115      * process that block using the passed callback.
12116      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12117      * for the request to the remote server.
12118      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12119      * object into a block of Roo.data.Records.
12120      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12121      * The function must be passed <ul>
12122      * <li>The Record block object</li>
12123      * <li>The "arg" argument from the load function</li>
12124      * <li>A boolean success indicator</li>
12125      * </ul>
12126      * @param {Object} scope The scope in which to call the callback
12127      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12128      */
12129     load : function(params, reader, callback, scope, arg){
12130         if(this.fireEvent("beforeload", this, params) !== false){
12131             var  o = {
12132                 params : params || {},
12133                 request: {
12134                     callback : callback,
12135                     scope : scope,
12136                     arg : arg
12137                 },
12138                 reader: reader,
12139                 callback : this.loadResponse,
12140                 scope: this
12141             };
12142             if(this.useAjax){
12143                 Roo.applyIf(o, this.conn);
12144                 if(this.activeRequest){
12145                     Roo.Ajax.abort(this.activeRequest);
12146                 }
12147                 this.activeRequest = Roo.Ajax.request(o);
12148             }else{
12149                 this.conn.request(o);
12150             }
12151         }else{
12152             callback.call(scope||this, null, arg, false);
12153         }
12154     },
12155
12156     // private
12157     loadResponse : function(o, success, response){
12158         delete this.activeRequest;
12159         if(!success){
12160             this.fireEvent("loadexception", this, o, response);
12161             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12162             return;
12163         }
12164         var result;
12165         try {
12166             result = o.reader.read(response);
12167         }catch(e){
12168             this.fireEvent("loadexception", this, o, response, e);
12169             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12170             return;
12171         }
12172         
12173         this.fireEvent("load", this, o, o.request.arg);
12174         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12175     },
12176
12177     // private
12178     update : function(dataSet){
12179
12180     },
12181
12182     // private
12183     updateResponse : function(dataSet){
12184
12185     }
12186 });/*
12187  * Based on:
12188  * Ext JS Library 1.1.1
12189  * Copyright(c) 2006-2007, Ext JS, LLC.
12190  *
12191  * Originally Released Under LGPL - original licence link has changed is not relivant.
12192  *
12193  * Fork - LGPL
12194  * <script type="text/javascript">
12195  */
12196
12197 /**
12198  * @class Roo.data.ScriptTagProxy
12199  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12200  * other than the originating domain of the running page.<br><br>
12201  * <p>
12202  * <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
12203  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12204  * <p>
12205  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12206  * source code that is used as the source inside a &lt;script> tag.<br><br>
12207  * <p>
12208  * In order for the browser to process the returned data, the server must wrap the data object
12209  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12210  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12211  * depending on whether the callback name was passed:
12212  * <p>
12213  * <pre><code>
12214 boolean scriptTag = false;
12215 String cb = request.getParameter("callback");
12216 if (cb != null) {
12217     scriptTag = true;
12218     response.setContentType("text/javascript");
12219 } else {
12220     response.setContentType("application/x-json");
12221 }
12222 Writer out = response.getWriter();
12223 if (scriptTag) {
12224     out.write(cb + "(");
12225 }
12226 out.print(dataBlock.toJsonString());
12227 if (scriptTag) {
12228     out.write(");");
12229 }
12230 </pre></code>
12231  *
12232  * @constructor
12233  * @param {Object} config A configuration object.
12234  */
12235 Roo.data.ScriptTagProxy = function(config){
12236     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12237     Roo.apply(this, config);
12238     this.head = document.getElementsByTagName("head")[0];
12239 };
12240
12241 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12242
12243 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12244     /**
12245      * @cfg {String} url The URL from which to request the data object.
12246      */
12247     /**
12248      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12249      */
12250     timeout : 30000,
12251     /**
12252      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12253      * the server the name of the callback function set up by the load call to process the returned data object.
12254      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12255      * javascript output which calls this named function passing the data object as its only parameter.
12256      */
12257     callbackParam : "callback",
12258     /**
12259      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12260      * name to the request.
12261      */
12262     nocache : true,
12263
12264     /**
12265      * Load data from the configured URL, read the data object into
12266      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12267      * process that block using the passed callback.
12268      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12269      * for the request to the remote server.
12270      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12271      * object into a block of Roo.data.Records.
12272      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12273      * The function must be passed <ul>
12274      * <li>The Record block object</li>
12275      * <li>The "arg" argument from the load function</li>
12276      * <li>A boolean success indicator</li>
12277      * </ul>
12278      * @param {Object} scope The scope in which to call the callback
12279      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12280      */
12281     load : function(params, reader, callback, scope, arg){
12282         if(this.fireEvent("beforeload", this, params) !== false){
12283
12284             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12285
12286             var url = this.url;
12287             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12288             if(this.nocache){
12289                 url += "&_dc=" + (new Date().getTime());
12290             }
12291             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12292             var trans = {
12293                 id : transId,
12294                 cb : "stcCallback"+transId,
12295                 scriptId : "stcScript"+transId,
12296                 params : params,
12297                 arg : arg,
12298                 url : url,
12299                 callback : callback,
12300                 scope : scope,
12301                 reader : reader
12302             };
12303             var conn = this;
12304
12305             window[trans.cb] = function(o){
12306                 conn.handleResponse(o, trans);
12307             };
12308
12309             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12310
12311             if(this.autoAbort !== false){
12312                 this.abort();
12313             }
12314
12315             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12316
12317             var script = document.createElement("script");
12318             script.setAttribute("src", url);
12319             script.setAttribute("type", "text/javascript");
12320             script.setAttribute("id", trans.scriptId);
12321             this.head.appendChild(script);
12322
12323             this.trans = trans;
12324         }else{
12325             callback.call(scope||this, null, arg, false);
12326         }
12327     },
12328
12329     // private
12330     isLoading : function(){
12331         return this.trans ? true : false;
12332     },
12333
12334     /**
12335      * Abort the current server request.
12336      */
12337     abort : function(){
12338         if(this.isLoading()){
12339             this.destroyTrans(this.trans);
12340         }
12341     },
12342
12343     // private
12344     destroyTrans : function(trans, isLoaded){
12345         this.head.removeChild(document.getElementById(trans.scriptId));
12346         clearTimeout(trans.timeoutId);
12347         if(isLoaded){
12348             window[trans.cb] = undefined;
12349             try{
12350                 delete window[trans.cb];
12351             }catch(e){}
12352         }else{
12353             // if hasn't been loaded, wait for load to remove it to prevent script error
12354             window[trans.cb] = function(){
12355                 window[trans.cb] = undefined;
12356                 try{
12357                     delete window[trans.cb];
12358                 }catch(e){}
12359             };
12360         }
12361     },
12362
12363     // private
12364     handleResponse : function(o, trans){
12365         this.trans = false;
12366         this.destroyTrans(trans, true);
12367         var result;
12368         try {
12369             result = trans.reader.readRecords(o);
12370         }catch(e){
12371             this.fireEvent("loadexception", this, o, trans.arg, e);
12372             trans.callback.call(trans.scope||window, null, trans.arg, false);
12373             return;
12374         }
12375         this.fireEvent("load", this, o, trans.arg);
12376         trans.callback.call(trans.scope||window, result, trans.arg, true);
12377     },
12378
12379     // private
12380     handleFailure : function(trans){
12381         this.trans = false;
12382         this.destroyTrans(trans, false);
12383         this.fireEvent("loadexception", this, null, trans.arg);
12384         trans.callback.call(trans.scope||window, null, trans.arg, false);
12385     }
12386 });/*
12387  * Based on:
12388  * Ext JS Library 1.1.1
12389  * Copyright(c) 2006-2007, Ext JS, LLC.
12390  *
12391  * Originally Released Under LGPL - original licence link has changed is not relivant.
12392  *
12393  * Fork - LGPL
12394  * <script type="text/javascript">
12395  */
12396
12397 /**
12398  * @class Roo.data.JsonReader
12399  * @extends Roo.data.DataReader
12400  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12401  * based on mappings in a provided Roo.data.Record constructor.
12402  * 
12403  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12404  * in the reply previously. 
12405  * 
12406  * <p>
12407  * Example code:
12408  * <pre><code>
12409 var RecordDef = Roo.data.Record.create([
12410     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12411     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12412 ]);
12413 var myReader = new Roo.data.JsonReader({
12414     totalProperty: "results",    // The property which contains the total dataset size (optional)
12415     root: "rows",                // The property which contains an Array of row objects
12416     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12417 }, RecordDef);
12418 </code></pre>
12419  * <p>
12420  * This would consume a JSON file like this:
12421  * <pre><code>
12422 { 'results': 2, 'rows': [
12423     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12424     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12425 }
12426 </code></pre>
12427  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12428  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12429  * paged from the remote server.
12430  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12431  * @cfg {String} root name of the property which contains the Array of row objects.
12432  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12433  * @cfg {Array} fields Array of field definition objects
12434  * @constructor
12435  * Create a new JsonReader
12436  * @param {Object} meta Metadata configuration options
12437  * @param {Object} recordType Either an Array of field definition objects,
12438  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12439  */
12440 Roo.data.JsonReader = function(meta, recordType){
12441     
12442     meta = meta || {};
12443     // set some defaults:
12444     Roo.applyIf(meta, {
12445         totalProperty: 'total',
12446         successProperty : 'success',
12447         root : 'data',
12448         id : 'id'
12449     });
12450     
12451     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12452 };
12453 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12454     
12455     /**
12456      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12457      * Used by Store query builder to append _requestMeta to params.
12458      * 
12459      */
12460     metaFromRemote : false,
12461     /**
12462      * This method is only used by a DataProxy which has retrieved data from a remote server.
12463      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12464      * @return {Object} data A data block which is used by an Roo.data.Store object as
12465      * a cache of Roo.data.Records.
12466      */
12467     read : function(response){
12468         var json = response.responseText;
12469        
12470         var o = /* eval:var:o */ eval("("+json+")");
12471         if(!o) {
12472             throw {message: "JsonReader.read: Json object not found"};
12473         }
12474         
12475         if(o.metaData){
12476             
12477             delete this.ef;
12478             this.metaFromRemote = true;
12479             this.meta = o.metaData;
12480             this.recordType = Roo.data.Record.create(o.metaData.fields);
12481             this.onMetaChange(this.meta, this.recordType, o);
12482         }
12483         return this.readRecords(o);
12484     },
12485
12486     // private function a store will implement
12487     onMetaChange : function(meta, recordType, o){
12488
12489     },
12490
12491     /**
12492          * @ignore
12493          */
12494     simpleAccess: function(obj, subsc) {
12495         return obj[subsc];
12496     },
12497
12498         /**
12499          * @ignore
12500          */
12501     getJsonAccessor: function(){
12502         var re = /[\[\.]/;
12503         return function(expr) {
12504             try {
12505                 return(re.test(expr))
12506                     ? new Function("obj", "return obj." + expr)
12507                     : function(obj){
12508                         return obj[expr];
12509                     };
12510             } catch(e){}
12511             return Roo.emptyFn;
12512         };
12513     }(),
12514
12515     /**
12516      * Create a data block containing Roo.data.Records from an XML document.
12517      * @param {Object} o An object which contains an Array of row objects in the property specified
12518      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12519      * which contains the total size of the dataset.
12520      * @return {Object} data A data block which is used by an Roo.data.Store object as
12521      * a cache of Roo.data.Records.
12522      */
12523     readRecords : function(o){
12524         /**
12525          * After any data loads, the raw JSON data is available for further custom processing.
12526          * @type Object
12527          */
12528         this.o = o;
12529         var s = this.meta, Record = this.recordType,
12530             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12531
12532 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12533         if (!this.ef) {
12534             if(s.totalProperty) {
12535                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12536                 }
12537                 if(s.successProperty) {
12538                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12539                 }
12540                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12541                 if (s.id) {
12542                         var g = this.getJsonAccessor(s.id);
12543                         this.getId = function(rec) {
12544                                 var r = g(rec);  
12545                                 return (r === undefined || r === "") ? null : r;
12546                         };
12547                 } else {
12548                         this.getId = function(){return null;};
12549                 }
12550             this.ef = [];
12551             for(var jj = 0; jj < fl; jj++){
12552                 f = fi[jj];
12553                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12554                 this.ef[jj] = this.getJsonAccessor(map);
12555             }
12556         }
12557
12558         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12559         if(s.totalProperty){
12560             var vt = parseInt(this.getTotal(o), 10);
12561             if(!isNaN(vt)){
12562                 totalRecords = vt;
12563             }
12564         }
12565         if(s.successProperty){
12566             var vs = this.getSuccess(o);
12567             if(vs === false || vs === 'false'){
12568                 success = false;
12569             }
12570         }
12571         var records = [];
12572         for(var i = 0; i < c; i++){
12573                 var n = root[i];
12574             var values = {};
12575             var id = this.getId(n);
12576             for(var j = 0; j < fl; j++){
12577                 f = fi[j];
12578             var v = this.ef[j](n);
12579             if (!f.convert) {
12580                 Roo.log('missing convert for ' + f.name);
12581                 Roo.log(f);
12582                 continue;
12583             }
12584             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12585             }
12586             var record = new Record(values, id);
12587             record.json = n;
12588             records[i] = record;
12589         }
12590         return {
12591             raw : o,
12592             success : success,
12593             records : records,
12594             totalRecords : totalRecords
12595         };
12596     }
12597 });/*
12598  * Based on:
12599  * Ext JS Library 1.1.1
12600  * Copyright(c) 2006-2007, Ext JS, LLC.
12601  *
12602  * Originally Released Under LGPL - original licence link has changed is not relivant.
12603  *
12604  * Fork - LGPL
12605  * <script type="text/javascript">
12606  */
12607
12608 /**
12609  * @class Roo.data.ArrayReader
12610  * @extends Roo.data.DataReader
12611  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12612  * Each element of that Array represents a row of data fields. The
12613  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12614  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12615  * <p>
12616  * Example code:.
12617  * <pre><code>
12618 var RecordDef = Roo.data.Record.create([
12619     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12620     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12621 ]);
12622 var myReader = new Roo.data.ArrayReader({
12623     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12624 }, RecordDef);
12625 </code></pre>
12626  * <p>
12627  * This would consume an Array like this:
12628  * <pre><code>
12629 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12630   </code></pre>
12631  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12632  * @constructor
12633  * Create a new JsonReader
12634  * @param {Object} meta Metadata configuration options.
12635  * @param {Object} recordType Either an Array of field definition objects
12636  * as specified to {@link Roo.data.Record#create},
12637  * or an {@link Roo.data.Record} object
12638  * created using {@link Roo.data.Record#create}.
12639  */
12640 Roo.data.ArrayReader = function(meta, recordType){
12641     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12642 };
12643
12644 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12645     /**
12646      * Create a data block containing Roo.data.Records from an XML document.
12647      * @param {Object} o An Array of row objects which represents the dataset.
12648      * @return {Object} data A data block which is used by an Roo.data.Store object as
12649      * a cache of Roo.data.Records.
12650      */
12651     readRecords : function(o){
12652         var sid = this.meta ? this.meta.id : null;
12653         var recordType = this.recordType, fields = recordType.prototype.fields;
12654         var records = [];
12655         var root = o;
12656             for(var i = 0; i < root.length; i++){
12657                     var n = root[i];
12658                 var values = {};
12659                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12660                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12661                 var f = fields.items[j];
12662                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12663                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12664                 v = f.convert(v);
12665                 values[f.name] = v;
12666             }
12667                 var record = new recordType(values, id);
12668                 record.json = n;
12669                 records[records.length] = record;
12670             }
12671             return {
12672                 records : records,
12673                 totalRecords : records.length
12674             };
12675     }
12676 });/*
12677  * - LGPL
12678  * * 
12679  */
12680
12681 /**
12682  * @class Roo.bootstrap.ComboBox
12683  * @extends Roo.bootstrap.TriggerField
12684  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12685  * @cfg {Boolean} append (true|false) default false
12686  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12687  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12688  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12689  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12690  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12691  * @cfg {Boolean} animate default true
12692  * @cfg {Boolean} emptyResultText only for touch device
12693  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12694  * @cfg {String} emptyTitle default ''
12695  * @constructor
12696  * Create a new ComboBox.
12697  * @param {Object} config Configuration options
12698  */
12699 Roo.bootstrap.ComboBox = function(config){
12700     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12701     this.addEvents({
12702         /**
12703          * @event expand
12704          * Fires when the dropdown list is expanded
12705         * @param {Roo.bootstrap.ComboBox} combo This combo box
12706         */
12707         'expand' : true,
12708         /**
12709          * @event collapse
12710          * Fires when the dropdown list is collapsed
12711         * @param {Roo.bootstrap.ComboBox} combo This combo box
12712         */
12713         'collapse' : true,
12714         /**
12715          * @event beforeselect
12716          * Fires before a list item is selected. Return false to cancel the selection.
12717         * @param {Roo.bootstrap.ComboBox} combo This combo box
12718         * @param {Roo.data.Record} record The data record returned from the underlying store
12719         * @param {Number} index The index of the selected item in the dropdown list
12720         */
12721         'beforeselect' : true,
12722         /**
12723          * @event select
12724          * Fires when a list item is selected
12725         * @param {Roo.bootstrap.ComboBox} combo This combo box
12726         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12727         * @param {Number} index The index of the selected item in the dropdown list
12728         */
12729         'select' : true,
12730         /**
12731          * @event beforequery
12732          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12733          * The event object passed has these properties:
12734         * @param {Roo.bootstrap.ComboBox} combo This combo box
12735         * @param {String} query The query
12736         * @param {Boolean} forceAll true to force "all" query
12737         * @param {Boolean} cancel true to cancel the query
12738         * @param {Object} e The query event object
12739         */
12740         'beforequery': true,
12741          /**
12742          * @event add
12743          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12744         * @param {Roo.bootstrap.ComboBox} combo This combo box
12745         */
12746         'add' : true,
12747         /**
12748          * @event edit
12749          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12750         * @param {Roo.bootstrap.ComboBox} combo This combo box
12751         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12752         */
12753         'edit' : true,
12754         /**
12755          * @event remove
12756          * Fires when the remove value from the combobox array
12757         * @param {Roo.bootstrap.ComboBox} combo This combo box
12758         */
12759         'remove' : true,
12760         /**
12761          * @event afterremove
12762          * Fires when the remove value from the combobox array
12763         * @param {Roo.bootstrap.ComboBox} combo This combo box
12764         */
12765         'afterremove' : true,
12766         /**
12767          * @event specialfilter
12768          * Fires when specialfilter
12769             * @param {Roo.bootstrap.ComboBox} combo This combo box
12770             */
12771         'specialfilter' : true,
12772         /**
12773          * @event tick
12774          * Fires when tick the element
12775             * @param {Roo.bootstrap.ComboBox} combo This combo box
12776             */
12777         'tick' : true,
12778         /**
12779          * @event touchviewdisplay
12780          * Fires when touch view require special display (default is using displayField)
12781             * @param {Roo.bootstrap.ComboBox} combo This combo box
12782             * @param {Object} cfg set html .
12783             */
12784         'touchviewdisplay' : true
12785         
12786     });
12787     
12788     this.item = [];
12789     this.tickItems = [];
12790     
12791     this.selectedIndex = -1;
12792     if(this.mode == 'local'){
12793         if(config.queryDelay === undefined){
12794             this.queryDelay = 10;
12795         }
12796         if(config.minChars === undefined){
12797             this.minChars = 0;
12798         }
12799     }
12800 };
12801
12802 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12803      
12804     /**
12805      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12806      * rendering into an Roo.Editor, defaults to false)
12807      */
12808     /**
12809      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12810      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12811      */
12812     /**
12813      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12814      */
12815     /**
12816      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12817      * the dropdown list (defaults to undefined, with no header element)
12818      */
12819
12820      /**
12821      * @cfg {String/Roo.Template} tpl The template to use to render the output
12822      */
12823      
12824      /**
12825      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12826      */
12827     listWidth: undefined,
12828     /**
12829      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12830      * mode = 'remote' or 'text' if mode = 'local')
12831      */
12832     displayField: undefined,
12833     
12834     /**
12835      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12836      * mode = 'remote' or 'value' if mode = 'local'). 
12837      * Note: use of a valueField requires the user make a selection
12838      * in order for a value to be mapped.
12839      */
12840     valueField: undefined,
12841     /**
12842      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12843      */
12844     modalTitle : '',
12845     
12846     /**
12847      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12848      * field's data value (defaults to the underlying DOM element's name)
12849      */
12850     hiddenName: undefined,
12851     /**
12852      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12853      */
12854     listClass: '',
12855     /**
12856      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12857      */
12858     selectedClass: 'active',
12859     
12860     /**
12861      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12862      */
12863     shadow:'sides',
12864     /**
12865      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12866      * anchor positions (defaults to 'tl-bl')
12867      */
12868     listAlign: 'tl-bl?',
12869     /**
12870      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12871      */
12872     maxHeight: 300,
12873     /**
12874      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12875      * query specified by the allQuery config option (defaults to 'query')
12876      */
12877     triggerAction: 'query',
12878     /**
12879      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12880      * (defaults to 4, does not apply if editable = false)
12881      */
12882     minChars : 4,
12883     /**
12884      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12885      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12886      */
12887     typeAhead: false,
12888     /**
12889      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12890      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12891      */
12892     queryDelay: 500,
12893     /**
12894      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12895      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12896      */
12897     pageSize: 0,
12898     /**
12899      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12900      * when editable = true (defaults to false)
12901      */
12902     selectOnFocus:false,
12903     /**
12904      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12905      */
12906     queryParam: 'query',
12907     /**
12908      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12909      * when mode = 'remote' (defaults to 'Loading...')
12910      */
12911     loadingText: 'Loading...',
12912     /**
12913      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12914      */
12915     resizable: false,
12916     /**
12917      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12918      */
12919     handleHeight : 8,
12920     /**
12921      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12922      * traditional select (defaults to true)
12923      */
12924     editable: true,
12925     /**
12926      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12927      */
12928     allQuery: '',
12929     /**
12930      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12931      */
12932     mode: 'remote',
12933     /**
12934      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12935      * listWidth has a higher value)
12936      */
12937     minListWidth : 70,
12938     /**
12939      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12940      * allow the user to set arbitrary text into the field (defaults to false)
12941      */
12942     forceSelection:false,
12943     /**
12944      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12945      * if typeAhead = true (defaults to 250)
12946      */
12947     typeAheadDelay : 250,
12948     /**
12949      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12950      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12951      */
12952     valueNotFoundText : undefined,
12953     /**
12954      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12955      */
12956     blockFocus : false,
12957     
12958     /**
12959      * @cfg {Boolean} disableClear Disable showing of clear button.
12960      */
12961     disableClear : false,
12962     /**
12963      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12964      */
12965     alwaysQuery : false,
12966     
12967     /**
12968      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12969      */
12970     multiple : false,
12971     
12972     /**
12973      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12974      */
12975     invalidClass : "has-warning",
12976     
12977     /**
12978      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12979      */
12980     validClass : "has-success",
12981     
12982     /**
12983      * @cfg {Boolean} specialFilter (true|false) special filter default false
12984      */
12985     specialFilter : false,
12986     
12987     /**
12988      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12989      */
12990     mobileTouchView : true,
12991     
12992     /**
12993      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12994      */
12995     useNativeIOS : false,
12996     
12997     ios_options : false,
12998     
12999     //private
13000     addicon : false,
13001     editicon: false,
13002     
13003     page: 0,
13004     hasQuery: false,
13005     append: false,
13006     loadNext: false,
13007     autoFocus : true,
13008     tickable : false,
13009     btnPosition : 'right',
13010     triggerList : true,
13011     showToggleBtn : true,
13012     animate : true,
13013     emptyResultText: 'Empty',
13014     triggerText : 'Select',
13015     emptyTitle : '',
13016     
13017     // element that contains real text value.. (when hidden is used..)
13018     
13019     getAutoCreate : function()
13020     {   
13021         var cfg = false;
13022         //render
13023         /*
13024          * Render classic select for iso
13025          */
13026         
13027         if(Roo.isIOS && this.useNativeIOS){
13028             cfg = this.getAutoCreateNativeIOS();
13029             return cfg;
13030         }
13031         
13032         /*
13033          * Touch Devices
13034          */
13035         
13036         if(Roo.isTouch && this.mobileTouchView){
13037             cfg = this.getAutoCreateTouchView();
13038             return cfg;;
13039         }
13040         
13041         /*
13042          *  Normal ComboBox
13043          */
13044         if(!this.tickable){
13045             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13046             return cfg;
13047         }
13048         
13049         /*
13050          *  ComboBox with tickable selections
13051          */
13052              
13053         var align = this.labelAlign || this.parentLabelAlign();
13054         
13055         cfg = {
13056             cls : 'form-group roo-combobox-tickable' //input-group
13057         };
13058         
13059         var btn_text_select = '';
13060         var btn_text_done = '';
13061         var btn_text_cancel = '';
13062         
13063         if (this.btn_text_show) {
13064             btn_text_select = 'Select';
13065             btn_text_done = 'Done';
13066             btn_text_cancel = 'Cancel'; 
13067         }
13068         
13069         var buttons = {
13070             tag : 'div',
13071             cls : 'tickable-buttons',
13072             cn : [
13073                 {
13074                     tag : 'button',
13075                     type : 'button',
13076                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13077                     //html : this.triggerText
13078                     html: btn_text_select
13079                 },
13080                 {
13081                     tag : 'button',
13082                     type : 'button',
13083                     name : 'ok',
13084                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13085                     //html : 'Done'
13086                     html: btn_text_done
13087                 },
13088                 {
13089                     tag : 'button',
13090                     type : 'button',
13091                     name : 'cancel',
13092                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13093                     //html : 'Cancel'
13094                     html: btn_text_cancel
13095                 }
13096             ]
13097         };
13098         
13099         if(this.editable){
13100             buttons.cn.unshift({
13101                 tag: 'input',
13102                 cls: 'roo-select2-search-field-input'
13103             });
13104         }
13105         
13106         var _this = this;
13107         
13108         Roo.each(buttons.cn, function(c){
13109             if (_this.size) {
13110                 c.cls += ' btn-' + _this.size;
13111             }
13112
13113             if (_this.disabled) {
13114                 c.disabled = true;
13115             }
13116         });
13117         
13118         var box = {
13119             tag: 'div',
13120             cn: [
13121                 {
13122                     tag: 'input',
13123                     type : 'hidden',
13124                     cls: 'form-hidden-field'
13125                 },
13126                 {
13127                     tag: 'ul',
13128                     cls: 'roo-select2-choices',
13129                     cn:[
13130                         {
13131                             tag: 'li',
13132                             cls: 'roo-select2-search-field',
13133                             cn: [
13134                                 buttons
13135                             ]
13136                         }
13137                     ]
13138                 }
13139             ]
13140         };
13141         
13142         var combobox = {
13143             cls: 'roo-select2-container input-group roo-select2-container-multi',
13144             cn: [
13145                 box
13146 //                {
13147 //                    tag: 'ul',
13148 //                    cls: 'typeahead typeahead-long dropdown-menu',
13149 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13150 //                }
13151             ]
13152         };
13153         
13154         if(this.hasFeedback && !this.allowBlank){
13155             
13156             var feedback = {
13157                 tag: 'span',
13158                 cls: 'glyphicon form-control-feedback'
13159             };
13160
13161             combobox.cn.push(feedback);
13162         }
13163         
13164         
13165         if (align ==='left' && this.fieldLabel.length) {
13166             
13167             cfg.cls += ' roo-form-group-label-left';
13168             
13169             cfg.cn = [
13170                 {
13171                     tag : 'i',
13172                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13173                     tooltip : 'This field is required'
13174                 },
13175                 {
13176                     tag: 'label',
13177                     'for' :  id,
13178                     cls : 'control-label',
13179                     html : this.fieldLabel
13180
13181                 },
13182                 {
13183                     cls : "", 
13184                     cn: [
13185                         combobox
13186                     ]
13187                 }
13188
13189             ];
13190             
13191             var labelCfg = cfg.cn[1];
13192             var contentCfg = cfg.cn[2];
13193             
13194
13195             if(this.indicatorpos == 'right'){
13196                 
13197                 cfg.cn = [
13198                     {
13199                         tag: 'label',
13200                         'for' :  id,
13201                         cls : 'control-label',
13202                         cn : [
13203                             {
13204                                 tag : 'span',
13205                                 html : this.fieldLabel
13206                             },
13207                             {
13208                                 tag : 'i',
13209                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13210                                 tooltip : 'This field is required'
13211                             }
13212                         ]
13213                     },
13214                     {
13215                         cls : "",
13216                         cn: [
13217                             combobox
13218                         ]
13219                     }
13220
13221                 ];
13222                 
13223                 
13224                 
13225                 labelCfg = cfg.cn[0];
13226                 contentCfg = cfg.cn[1];
13227             
13228             }
13229             
13230             if(this.labelWidth > 12){
13231                 labelCfg.style = "width: " + this.labelWidth + 'px';
13232             }
13233             
13234             if(this.labelWidth < 13 && this.labelmd == 0){
13235                 this.labelmd = this.labelWidth;
13236             }
13237             
13238             if(this.labellg > 0){
13239                 labelCfg.cls += ' col-lg-' + this.labellg;
13240                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13241             }
13242             
13243             if(this.labelmd > 0){
13244                 labelCfg.cls += ' col-md-' + this.labelmd;
13245                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13246             }
13247             
13248             if(this.labelsm > 0){
13249                 labelCfg.cls += ' col-sm-' + this.labelsm;
13250                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13251             }
13252             
13253             if(this.labelxs > 0){
13254                 labelCfg.cls += ' col-xs-' + this.labelxs;
13255                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13256             }
13257                 
13258                 
13259         } else if ( this.fieldLabel.length) {
13260 //                Roo.log(" label");
13261                  cfg.cn = [
13262                     {
13263                         tag : 'i',
13264                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13265                         tooltip : 'This field is required'
13266                     },
13267                     {
13268                         tag: 'label',
13269                         //cls : 'input-group-addon',
13270                         html : this.fieldLabel
13271                     },
13272                     combobox
13273                 ];
13274                 
13275                 if(this.indicatorpos == 'right'){
13276                     cfg.cn = [
13277                         {
13278                             tag: 'label',
13279                             //cls : 'input-group-addon',
13280                             html : this.fieldLabel
13281                         },
13282                         {
13283                             tag : 'i',
13284                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13285                             tooltip : 'This field is required'
13286                         },
13287                         combobox
13288                     ];
13289                     
13290                 }
13291
13292         } else {
13293             
13294 //                Roo.log(" no label && no align");
13295                 cfg = combobox
13296                      
13297                 
13298         }
13299          
13300         var settings=this;
13301         ['xs','sm','md','lg'].map(function(size){
13302             if (settings[size]) {
13303                 cfg.cls += ' col-' + size + '-' + settings[size];
13304             }
13305         });
13306         
13307         return cfg;
13308         
13309     },
13310     
13311     _initEventsCalled : false,
13312     
13313     // private
13314     initEvents: function()
13315     {   
13316         if (this._initEventsCalled) { // as we call render... prevent looping...
13317             return;
13318         }
13319         this._initEventsCalled = true;
13320         
13321         if (!this.store) {
13322             throw "can not find store for combo";
13323         }
13324         
13325         this.indicator = this.indicatorEl();
13326         
13327         this.store = Roo.factory(this.store, Roo.data);
13328         this.store.parent = this;
13329         
13330         // if we are building from html. then this element is so complex, that we can not really
13331         // use the rendered HTML.
13332         // so we have to trash and replace the previous code.
13333         if (Roo.XComponent.build_from_html) {
13334             // remove this element....
13335             var e = this.el.dom, k=0;
13336             while (e ) { e = e.previousSibling;  ++k;}
13337
13338             this.el.remove();
13339             
13340             this.el=false;
13341             this.rendered = false;
13342             
13343             this.render(this.parent().getChildContainer(true), k);
13344         }
13345         
13346         if(Roo.isIOS && this.useNativeIOS){
13347             this.initIOSView();
13348             return;
13349         }
13350         
13351         /*
13352          * Touch Devices
13353          */
13354         
13355         if(Roo.isTouch && this.mobileTouchView){
13356             this.initTouchView();
13357             return;
13358         }
13359         
13360         if(this.tickable){
13361             this.initTickableEvents();
13362             return;
13363         }
13364         
13365         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13366         
13367         if(this.hiddenName){
13368             
13369             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13370             
13371             this.hiddenField.dom.value =
13372                 this.hiddenValue !== undefined ? this.hiddenValue :
13373                 this.value !== undefined ? this.value : '';
13374
13375             // prevent input submission
13376             this.el.dom.removeAttribute('name');
13377             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13378              
13379              
13380         }
13381         //if(Roo.isGecko){
13382         //    this.el.dom.setAttribute('autocomplete', 'off');
13383         //}
13384         
13385         var cls = 'x-combo-list';
13386         
13387         //this.list = new Roo.Layer({
13388         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13389         //});
13390         
13391         var _this = this;
13392         
13393         (function(){
13394             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13395             _this.list.setWidth(lw);
13396         }).defer(100);
13397         
13398         this.list.on('mouseover', this.onViewOver, this);
13399         this.list.on('mousemove', this.onViewMove, this);
13400         this.list.on('scroll', this.onViewScroll, this);
13401         
13402         /*
13403         this.list.swallowEvent('mousewheel');
13404         this.assetHeight = 0;
13405
13406         if(this.title){
13407             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13408             this.assetHeight += this.header.getHeight();
13409         }
13410
13411         this.innerList = this.list.createChild({cls:cls+'-inner'});
13412         this.innerList.on('mouseover', this.onViewOver, this);
13413         this.innerList.on('mousemove', this.onViewMove, this);
13414         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13415         
13416         if(this.allowBlank && !this.pageSize && !this.disableClear){
13417             this.footer = this.list.createChild({cls:cls+'-ft'});
13418             this.pageTb = new Roo.Toolbar(this.footer);
13419            
13420         }
13421         if(this.pageSize){
13422             this.footer = this.list.createChild({cls:cls+'-ft'});
13423             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13424                     {pageSize: this.pageSize});
13425             
13426         }
13427         
13428         if (this.pageTb && this.allowBlank && !this.disableClear) {
13429             var _this = this;
13430             this.pageTb.add(new Roo.Toolbar.Fill(), {
13431                 cls: 'x-btn-icon x-btn-clear',
13432                 text: '&#160;',
13433                 handler: function()
13434                 {
13435                     _this.collapse();
13436                     _this.clearValue();
13437                     _this.onSelect(false, -1);
13438                 }
13439             });
13440         }
13441         if (this.footer) {
13442             this.assetHeight += this.footer.getHeight();
13443         }
13444         */
13445             
13446         if(!this.tpl){
13447             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13448         }
13449
13450         this.view = new Roo.View(this.list, this.tpl, {
13451             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13452         });
13453         //this.view.wrapEl.setDisplayed(false);
13454         this.view.on('click', this.onViewClick, this);
13455         
13456         
13457         this.store.on('beforeload', this.onBeforeLoad, this);
13458         this.store.on('load', this.onLoad, this);
13459         this.store.on('loadexception', this.onLoadException, this);
13460         /*
13461         if(this.resizable){
13462             this.resizer = new Roo.Resizable(this.list,  {
13463                pinned:true, handles:'se'
13464             });
13465             this.resizer.on('resize', function(r, w, h){
13466                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13467                 this.listWidth = w;
13468                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13469                 this.restrictHeight();
13470             }, this);
13471             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13472         }
13473         */
13474         if(!this.editable){
13475             this.editable = true;
13476             this.setEditable(false);
13477         }
13478         
13479         /*
13480         
13481         if (typeof(this.events.add.listeners) != 'undefined') {
13482             
13483             this.addicon = this.wrap.createChild(
13484                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13485        
13486             this.addicon.on('click', function(e) {
13487                 this.fireEvent('add', this);
13488             }, this);
13489         }
13490         if (typeof(this.events.edit.listeners) != 'undefined') {
13491             
13492             this.editicon = this.wrap.createChild(
13493                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13494             if (this.addicon) {
13495                 this.editicon.setStyle('margin-left', '40px');
13496             }
13497             this.editicon.on('click', function(e) {
13498                 
13499                 // we fire even  if inothing is selected..
13500                 this.fireEvent('edit', this, this.lastData );
13501                 
13502             }, this);
13503         }
13504         */
13505         
13506         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13507             "up" : function(e){
13508                 this.inKeyMode = true;
13509                 this.selectPrev();
13510             },
13511
13512             "down" : function(e){
13513                 if(!this.isExpanded()){
13514                     this.onTriggerClick();
13515                 }else{
13516                     this.inKeyMode = true;
13517                     this.selectNext();
13518                 }
13519             },
13520
13521             "enter" : function(e){
13522 //                this.onViewClick();
13523                 //return true;
13524                 this.collapse();
13525                 
13526                 if(this.fireEvent("specialkey", this, e)){
13527                     this.onViewClick(false);
13528                 }
13529                 
13530                 return true;
13531             },
13532
13533             "esc" : function(e){
13534                 this.collapse();
13535             },
13536
13537             "tab" : function(e){
13538                 this.collapse();
13539                 
13540                 if(this.fireEvent("specialkey", this, e)){
13541                     this.onViewClick(false);
13542                 }
13543                 
13544                 return true;
13545             },
13546
13547             scope : this,
13548
13549             doRelay : function(foo, bar, hname){
13550                 if(hname == 'down' || this.scope.isExpanded()){
13551                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13552                 }
13553                 return true;
13554             },
13555
13556             forceKeyDown: true
13557         });
13558         
13559         
13560         this.queryDelay = Math.max(this.queryDelay || 10,
13561                 this.mode == 'local' ? 10 : 250);
13562         
13563         
13564         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13565         
13566         if(this.typeAhead){
13567             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13568         }
13569         if(this.editable !== false){
13570             this.inputEl().on("keyup", this.onKeyUp, this);
13571         }
13572         if(this.forceSelection){
13573             this.inputEl().on('blur', this.doForce, this);
13574         }
13575         
13576         if(this.multiple){
13577             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13578             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13579         }
13580     },
13581     
13582     initTickableEvents: function()
13583     {   
13584         this.createList();
13585         
13586         if(this.hiddenName){
13587             
13588             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13589             
13590             this.hiddenField.dom.value =
13591                 this.hiddenValue !== undefined ? this.hiddenValue :
13592                 this.value !== undefined ? this.value : '';
13593
13594             // prevent input submission
13595             this.el.dom.removeAttribute('name');
13596             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13597              
13598              
13599         }
13600         
13601 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13602         
13603         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13604         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13605         if(this.triggerList){
13606             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13607         }
13608          
13609         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13610         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13611         
13612         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13613         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13614         
13615         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13616         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13617         
13618         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13619         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13620         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13621         
13622         this.okBtn.hide();
13623         this.cancelBtn.hide();
13624         
13625         var _this = this;
13626         
13627         (function(){
13628             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13629             _this.list.setWidth(lw);
13630         }).defer(100);
13631         
13632         this.list.on('mouseover', this.onViewOver, this);
13633         this.list.on('mousemove', this.onViewMove, this);
13634         
13635         this.list.on('scroll', this.onViewScroll, this);
13636         
13637         if(!this.tpl){
13638             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13639                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13640         }
13641
13642         this.view = new Roo.View(this.list, this.tpl, {
13643             singleSelect:true,
13644             tickable:true,
13645             parent:this,
13646             store: this.store,
13647             selectedClass: this.selectedClass
13648         });
13649         
13650         //this.view.wrapEl.setDisplayed(false);
13651         this.view.on('click', this.onViewClick, this);
13652         
13653         
13654         
13655         this.store.on('beforeload', this.onBeforeLoad, this);
13656         this.store.on('load', this.onLoad, this);
13657         this.store.on('loadexception', this.onLoadException, this);
13658         
13659         if(this.editable){
13660             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13661                 "up" : function(e){
13662                     this.inKeyMode = true;
13663                     this.selectPrev();
13664                 },
13665
13666                 "down" : function(e){
13667                     this.inKeyMode = true;
13668                     this.selectNext();
13669                 },
13670
13671                 "enter" : function(e){
13672                     if(this.fireEvent("specialkey", this, e)){
13673                         this.onViewClick(false);
13674                     }
13675                     
13676                     return true;
13677                 },
13678
13679                 "esc" : function(e){
13680                     this.onTickableFooterButtonClick(e, false, false);
13681                 },
13682
13683                 "tab" : function(e){
13684                     this.fireEvent("specialkey", this, e);
13685                     
13686                     this.onTickableFooterButtonClick(e, false, false);
13687                     
13688                     return true;
13689                 },
13690
13691                 scope : this,
13692
13693                 doRelay : function(e, fn, key){
13694                     if(this.scope.isExpanded()){
13695                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13696                     }
13697                     return true;
13698                 },
13699
13700                 forceKeyDown: true
13701             });
13702         }
13703         
13704         this.queryDelay = Math.max(this.queryDelay || 10,
13705                 this.mode == 'local' ? 10 : 250);
13706         
13707         
13708         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13709         
13710         if(this.typeAhead){
13711             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13712         }
13713         
13714         if(this.editable !== false){
13715             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13716         }
13717         
13718         this.indicator = this.indicatorEl();
13719         
13720         if(this.indicator){
13721             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13722             this.indicator.hide();
13723         }
13724         
13725     },
13726
13727     onDestroy : function(){
13728         if(this.view){
13729             this.view.setStore(null);
13730             this.view.el.removeAllListeners();
13731             this.view.el.remove();
13732             this.view.purgeListeners();
13733         }
13734         if(this.list){
13735             this.list.dom.innerHTML  = '';
13736         }
13737         
13738         if(this.store){
13739             this.store.un('beforeload', this.onBeforeLoad, this);
13740             this.store.un('load', this.onLoad, this);
13741             this.store.un('loadexception', this.onLoadException, this);
13742         }
13743         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13744     },
13745
13746     // private
13747     fireKey : function(e){
13748         if(e.isNavKeyPress() && !this.list.isVisible()){
13749             this.fireEvent("specialkey", this, e);
13750         }
13751     },
13752
13753     // private
13754     onResize: function(w, h){
13755 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13756 //        
13757 //        if(typeof w != 'number'){
13758 //            // we do not handle it!?!?
13759 //            return;
13760 //        }
13761 //        var tw = this.trigger.getWidth();
13762 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13763 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13764 //        var x = w - tw;
13765 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13766 //            
13767 //        //this.trigger.setStyle('left', x+'px');
13768 //        
13769 //        if(this.list && this.listWidth === undefined){
13770 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13771 //            this.list.setWidth(lw);
13772 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13773 //        }
13774         
13775     
13776         
13777     },
13778
13779     /**
13780      * Allow or prevent the user from directly editing the field text.  If false is passed,
13781      * the user will only be able to select from the items defined in the dropdown list.  This method
13782      * is the runtime equivalent of setting the 'editable' config option at config time.
13783      * @param {Boolean} value True to allow the user to directly edit the field text
13784      */
13785     setEditable : function(value){
13786         if(value == this.editable){
13787             return;
13788         }
13789         this.editable = value;
13790         if(!value){
13791             this.inputEl().dom.setAttribute('readOnly', true);
13792             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13793             this.inputEl().addClass('x-combo-noedit');
13794         }else{
13795             this.inputEl().dom.setAttribute('readOnly', false);
13796             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13797             this.inputEl().removeClass('x-combo-noedit');
13798         }
13799     },
13800
13801     // private
13802     
13803     onBeforeLoad : function(combo,opts){
13804         if(!this.hasFocus){
13805             return;
13806         }
13807          if (!opts.add) {
13808             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13809          }
13810         this.restrictHeight();
13811         this.selectedIndex = -1;
13812     },
13813
13814     // private
13815     onLoad : function(){
13816         
13817         this.hasQuery = false;
13818         
13819         if(!this.hasFocus){
13820             return;
13821         }
13822         
13823         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13824             this.loading.hide();
13825         }
13826         
13827         if(this.store.getCount() > 0){
13828             
13829             this.expand();
13830             this.restrictHeight();
13831             if(this.lastQuery == this.allQuery){
13832                 if(this.editable && !this.tickable){
13833                     this.inputEl().dom.select();
13834                 }
13835                 
13836                 if(
13837                     !this.selectByValue(this.value, true) &&
13838                     this.autoFocus && 
13839                     (
13840                         !this.store.lastOptions ||
13841                         typeof(this.store.lastOptions.add) == 'undefined' || 
13842                         this.store.lastOptions.add != true
13843                     )
13844                 ){
13845                     this.select(0, true);
13846                 }
13847             }else{
13848                 if(this.autoFocus){
13849                     this.selectNext();
13850                 }
13851                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13852                     this.taTask.delay(this.typeAheadDelay);
13853                 }
13854             }
13855         }else{
13856             this.onEmptyResults();
13857         }
13858         
13859         //this.el.focus();
13860     },
13861     // private
13862     onLoadException : function()
13863     {
13864         this.hasQuery = false;
13865         
13866         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13867             this.loading.hide();
13868         }
13869         
13870         if(this.tickable && this.editable){
13871             return;
13872         }
13873         
13874         this.collapse();
13875         // only causes errors at present
13876         //Roo.log(this.store.reader.jsonData);
13877         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13878             // fixme
13879             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13880         //}
13881         
13882         
13883     },
13884     // private
13885     onTypeAhead : function(){
13886         if(this.store.getCount() > 0){
13887             var r = this.store.getAt(0);
13888             var newValue = r.data[this.displayField];
13889             var len = newValue.length;
13890             var selStart = this.getRawValue().length;
13891             
13892             if(selStart != len){
13893                 this.setRawValue(newValue);
13894                 this.selectText(selStart, newValue.length);
13895             }
13896         }
13897     },
13898
13899     // private
13900     onSelect : function(record, index){
13901         
13902         if(this.fireEvent('beforeselect', this, record, index) !== false){
13903         
13904             this.setFromData(index > -1 ? record.data : false);
13905             
13906             this.collapse();
13907             this.fireEvent('select', this, record, index);
13908         }
13909     },
13910
13911     /**
13912      * Returns the currently selected field value or empty string if no value is set.
13913      * @return {String} value The selected value
13914      */
13915     getValue : function()
13916     {
13917         if(Roo.isIOS && this.useNativeIOS){
13918             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13919         }
13920         
13921         if(this.multiple){
13922             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13923         }
13924         
13925         if(this.valueField){
13926             return typeof this.value != 'undefined' ? this.value : '';
13927         }else{
13928             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13929         }
13930     },
13931     
13932     getRawValue : function()
13933     {
13934         if(Roo.isIOS && this.useNativeIOS){
13935             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13936         }
13937         
13938         var v = this.inputEl().getValue();
13939         
13940         return v;
13941     },
13942
13943     /**
13944      * Clears any text/value currently set in the field
13945      */
13946     clearValue : function(){
13947         
13948         if(this.hiddenField){
13949             this.hiddenField.dom.value = '';
13950         }
13951         this.value = '';
13952         this.setRawValue('');
13953         this.lastSelectionText = '';
13954         this.lastData = false;
13955         
13956         var close = this.closeTriggerEl();
13957         
13958         if(close){
13959             close.hide();
13960         }
13961         
13962         this.validate();
13963         
13964     },
13965
13966     /**
13967      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13968      * will be displayed in the field.  If the value does not match the data value of an existing item,
13969      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13970      * Otherwise the field will be blank (although the value will still be set).
13971      * @param {String} value The value to match
13972      */
13973     setValue : function(v)
13974     {
13975         if(Roo.isIOS && this.useNativeIOS){
13976             this.setIOSValue(v);
13977             return;
13978         }
13979         
13980         if(this.multiple){
13981             this.syncValue();
13982             return;
13983         }
13984         
13985         var text = v;
13986         if(this.valueField){
13987             var r = this.findRecord(this.valueField, v);
13988             if(r){
13989                 text = r.data[this.displayField];
13990             }else if(this.valueNotFoundText !== undefined){
13991                 text = this.valueNotFoundText;
13992             }
13993         }
13994         this.lastSelectionText = text;
13995         if(this.hiddenField){
13996             this.hiddenField.dom.value = v;
13997         }
13998         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13999         this.value = v;
14000         
14001         var close = this.closeTriggerEl();
14002         
14003         if(close){
14004             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14005         }
14006         
14007         this.validate();
14008     },
14009     /**
14010      * @property {Object} the last set data for the element
14011      */
14012     
14013     lastData : false,
14014     /**
14015      * Sets the value of the field based on a object which is related to the record format for the store.
14016      * @param {Object} value the value to set as. or false on reset?
14017      */
14018     setFromData : function(o){
14019         
14020         if(this.multiple){
14021             this.addItem(o);
14022             return;
14023         }
14024             
14025         var dv = ''; // display value
14026         var vv = ''; // value value..
14027         this.lastData = o;
14028         if (this.displayField) {
14029             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14030         } else {
14031             // this is an error condition!!!
14032             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14033         }
14034         
14035         if(this.valueField){
14036             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14037         }
14038         
14039         var close = this.closeTriggerEl();
14040         
14041         if(close){
14042             if(dv.length || vv * 1 > 0){
14043                 close.show() ;
14044                 this.blockFocus=true;
14045             } else {
14046                 close.hide();
14047             }             
14048         }
14049         
14050         if(this.hiddenField){
14051             this.hiddenField.dom.value = vv;
14052             
14053             this.lastSelectionText = dv;
14054             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14055             this.value = vv;
14056             return;
14057         }
14058         // no hidden field.. - we store the value in 'value', but still display
14059         // display field!!!!
14060         this.lastSelectionText = dv;
14061         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14062         this.value = vv;
14063         
14064         
14065         
14066     },
14067     // private
14068     reset : function(){
14069         // overridden so that last data is reset..
14070         
14071         if(this.multiple){
14072             this.clearItem();
14073             return;
14074         }
14075         
14076         this.setValue(this.originalValue);
14077         //this.clearInvalid();
14078         this.lastData = false;
14079         if (this.view) {
14080             this.view.clearSelections();
14081         }
14082         
14083         this.validate();
14084     },
14085     // private
14086     findRecord : function(prop, value){
14087         var record;
14088         if(this.store.getCount() > 0){
14089             this.store.each(function(r){
14090                 if(r.data[prop] == value){
14091                     record = r;
14092                     return false;
14093                 }
14094                 return true;
14095             });
14096         }
14097         return record;
14098     },
14099     
14100     getName: function()
14101     {
14102         // returns hidden if it's set..
14103         if (!this.rendered) {return ''};
14104         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14105         
14106     },
14107     // private
14108     onViewMove : function(e, t){
14109         this.inKeyMode = false;
14110     },
14111
14112     // private
14113     onViewOver : function(e, t){
14114         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14115             return;
14116         }
14117         var item = this.view.findItemFromChild(t);
14118         
14119         if(item){
14120             var index = this.view.indexOf(item);
14121             this.select(index, false);
14122         }
14123     },
14124
14125     // private
14126     onViewClick : function(view, doFocus, el, e)
14127     {
14128         var index = this.view.getSelectedIndexes()[0];
14129         
14130         var r = this.store.getAt(index);
14131         
14132         if(this.tickable){
14133             
14134             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14135                 return;
14136             }
14137             
14138             var rm = false;
14139             var _this = this;
14140             
14141             Roo.each(this.tickItems, function(v,k){
14142                 
14143                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14144                     Roo.log(v);
14145                     _this.tickItems.splice(k, 1);
14146                     
14147                     if(typeof(e) == 'undefined' && view == false){
14148                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14149                     }
14150                     
14151                     rm = true;
14152                     return;
14153                 }
14154             });
14155             
14156             if(rm){
14157                 return;
14158             }
14159             
14160             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14161                 this.tickItems.push(r.data);
14162             }
14163             
14164             if(typeof(e) == 'undefined' && view == false){
14165                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14166             }
14167                     
14168             return;
14169         }
14170         
14171         if(r){
14172             this.onSelect(r, index);
14173         }
14174         if(doFocus !== false && !this.blockFocus){
14175             this.inputEl().focus();
14176         }
14177     },
14178
14179     // private
14180     restrictHeight : function(){
14181         //this.innerList.dom.style.height = '';
14182         //var inner = this.innerList.dom;
14183         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14184         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14185         //this.list.beginUpdate();
14186         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14187         this.list.alignTo(this.inputEl(), this.listAlign);
14188         this.list.alignTo(this.inputEl(), this.listAlign);
14189         //this.list.endUpdate();
14190     },
14191
14192     // private
14193     onEmptyResults : function(){
14194         
14195         if(this.tickable && this.editable){
14196             this.hasFocus = false;
14197             this.restrictHeight();
14198             return;
14199         }
14200         
14201         this.collapse();
14202     },
14203
14204     /**
14205      * Returns true if the dropdown list is expanded, else false.
14206      */
14207     isExpanded : function(){
14208         return this.list.isVisible();
14209     },
14210
14211     /**
14212      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14213      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14214      * @param {String} value The data value of the item to select
14215      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14216      * selected item if it is not currently in view (defaults to true)
14217      * @return {Boolean} True if the value matched an item in the list, else false
14218      */
14219     selectByValue : function(v, scrollIntoView){
14220         if(v !== undefined && v !== null){
14221             var r = this.findRecord(this.valueField || this.displayField, v);
14222             if(r){
14223                 this.select(this.store.indexOf(r), scrollIntoView);
14224                 return true;
14225             }
14226         }
14227         return false;
14228     },
14229
14230     /**
14231      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14232      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14233      * @param {Number} index The zero-based index of the list item to select
14234      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14235      * selected item if it is not currently in view (defaults to true)
14236      */
14237     select : function(index, scrollIntoView){
14238         this.selectedIndex = index;
14239         this.view.select(index);
14240         if(scrollIntoView !== false){
14241             var el = this.view.getNode(index);
14242             /*
14243              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14244              */
14245             if(el){
14246                 this.list.scrollChildIntoView(el, false);
14247             }
14248         }
14249     },
14250
14251     // private
14252     selectNext : function(){
14253         var ct = this.store.getCount();
14254         if(ct > 0){
14255             if(this.selectedIndex == -1){
14256                 this.select(0);
14257             }else if(this.selectedIndex < ct-1){
14258                 this.select(this.selectedIndex+1);
14259             }
14260         }
14261     },
14262
14263     // private
14264     selectPrev : function(){
14265         var ct = this.store.getCount();
14266         if(ct > 0){
14267             if(this.selectedIndex == -1){
14268                 this.select(0);
14269             }else if(this.selectedIndex != 0){
14270                 this.select(this.selectedIndex-1);
14271             }
14272         }
14273     },
14274
14275     // private
14276     onKeyUp : function(e){
14277         if(this.editable !== false && !e.isSpecialKey()){
14278             this.lastKey = e.getKey();
14279             this.dqTask.delay(this.queryDelay);
14280         }
14281     },
14282
14283     // private
14284     validateBlur : function(){
14285         return !this.list || !this.list.isVisible();   
14286     },
14287
14288     // private
14289     initQuery : function(){
14290         
14291         var v = this.getRawValue();
14292         
14293         if(this.tickable && this.editable){
14294             v = this.tickableInputEl().getValue();
14295         }
14296         
14297         this.doQuery(v);
14298     },
14299
14300     // private
14301     doForce : function(){
14302         if(this.inputEl().dom.value.length > 0){
14303             this.inputEl().dom.value =
14304                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14305              
14306         }
14307     },
14308
14309     /**
14310      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14311      * query allowing the query action to be canceled if needed.
14312      * @param {String} query The SQL query to execute
14313      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14314      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14315      * saved in the current store (defaults to false)
14316      */
14317     doQuery : function(q, forceAll){
14318         
14319         if(q === undefined || q === null){
14320             q = '';
14321         }
14322         var qe = {
14323             query: q,
14324             forceAll: forceAll,
14325             combo: this,
14326             cancel:false
14327         };
14328         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14329             return false;
14330         }
14331         q = qe.query;
14332         
14333         forceAll = qe.forceAll;
14334         if(forceAll === true || (q.length >= this.minChars)){
14335             
14336             this.hasQuery = true;
14337             
14338             if(this.lastQuery != q || this.alwaysQuery){
14339                 this.lastQuery = q;
14340                 if(this.mode == 'local'){
14341                     this.selectedIndex = -1;
14342                     if(forceAll){
14343                         this.store.clearFilter();
14344                     }else{
14345                         
14346                         if(this.specialFilter){
14347                             this.fireEvent('specialfilter', this);
14348                             this.onLoad();
14349                             return;
14350                         }
14351                         
14352                         this.store.filter(this.displayField, q);
14353                     }
14354                     
14355                     this.store.fireEvent("datachanged", this.store);
14356                     
14357                     this.onLoad();
14358                     
14359                     
14360                 }else{
14361                     
14362                     this.store.baseParams[this.queryParam] = q;
14363                     
14364                     var options = {params : this.getParams(q)};
14365                     
14366                     if(this.loadNext){
14367                         options.add = true;
14368                         options.params.start = this.page * this.pageSize;
14369                     }
14370                     
14371                     this.store.load(options);
14372                     
14373                     /*
14374                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14375                      *  we should expand the list on onLoad
14376                      *  so command out it
14377                      */
14378 //                    this.expand();
14379                 }
14380             }else{
14381                 this.selectedIndex = -1;
14382                 this.onLoad();   
14383             }
14384         }
14385         
14386         this.loadNext = false;
14387     },
14388     
14389     // private
14390     getParams : function(q){
14391         var p = {};
14392         //p[this.queryParam] = q;
14393         
14394         if(this.pageSize){
14395             p.start = 0;
14396             p.limit = this.pageSize;
14397         }
14398         return p;
14399     },
14400
14401     /**
14402      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14403      */
14404     collapse : function(){
14405         if(!this.isExpanded()){
14406             return;
14407         }
14408         
14409         this.list.hide();
14410         
14411         this.hasFocus = false;
14412         
14413         if(this.tickable){
14414             this.okBtn.hide();
14415             this.cancelBtn.hide();
14416             this.trigger.show();
14417             
14418             if(this.editable){
14419                 this.tickableInputEl().dom.value = '';
14420                 this.tickableInputEl().blur();
14421             }
14422             
14423         }
14424         
14425         Roo.get(document).un('mousedown', this.collapseIf, this);
14426         Roo.get(document).un('mousewheel', this.collapseIf, this);
14427         if (!this.editable) {
14428             Roo.get(document).un('keydown', this.listKeyPress, this);
14429         }
14430         this.fireEvent('collapse', this);
14431         
14432         this.validate();
14433     },
14434
14435     // private
14436     collapseIf : function(e){
14437         var in_combo  = e.within(this.el);
14438         var in_list =  e.within(this.list);
14439         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14440         
14441         if (in_combo || in_list || is_list) {
14442             //e.stopPropagation();
14443             return;
14444         }
14445         
14446         if(this.tickable){
14447             this.onTickableFooterButtonClick(e, false, false);
14448         }
14449
14450         this.collapse();
14451         
14452     },
14453
14454     /**
14455      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14456      */
14457     expand : function(){
14458        
14459         if(this.isExpanded() || !this.hasFocus){
14460             return;
14461         }
14462         
14463         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14464         this.list.setWidth(lw);
14465         
14466         Roo.log('expand');
14467         
14468         this.list.show();
14469         
14470         this.restrictHeight();
14471         
14472         if(this.tickable){
14473             
14474             this.tickItems = Roo.apply([], this.item);
14475             
14476             this.okBtn.show();
14477             this.cancelBtn.show();
14478             this.trigger.hide();
14479             
14480             if(this.editable){
14481                 this.tickableInputEl().focus();
14482             }
14483             
14484         }
14485         
14486         Roo.get(document).on('mousedown', this.collapseIf, this);
14487         Roo.get(document).on('mousewheel', this.collapseIf, this);
14488         if (!this.editable) {
14489             Roo.get(document).on('keydown', this.listKeyPress, this);
14490         }
14491         
14492         this.fireEvent('expand', this);
14493     },
14494
14495     // private
14496     // Implements the default empty TriggerField.onTriggerClick function
14497     onTriggerClick : function(e)
14498     {
14499         Roo.log('trigger click');
14500         
14501         if(this.disabled || !this.triggerList){
14502             return;
14503         }
14504         
14505         this.page = 0;
14506         this.loadNext = false;
14507         
14508         if(this.isExpanded()){
14509             this.collapse();
14510             if (!this.blockFocus) {
14511                 this.inputEl().focus();
14512             }
14513             
14514         }else {
14515             this.hasFocus = true;
14516             if(this.triggerAction == 'all') {
14517                 this.doQuery(this.allQuery, true);
14518             } else {
14519                 this.doQuery(this.getRawValue());
14520             }
14521             if (!this.blockFocus) {
14522                 this.inputEl().focus();
14523             }
14524         }
14525     },
14526     
14527     onTickableTriggerClick : function(e)
14528     {
14529         if(this.disabled){
14530             return;
14531         }
14532         
14533         this.page = 0;
14534         this.loadNext = false;
14535         this.hasFocus = true;
14536         
14537         if(this.triggerAction == 'all') {
14538             this.doQuery(this.allQuery, true);
14539         } else {
14540             this.doQuery(this.getRawValue());
14541         }
14542     },
14543     
14544     onSearchFieldClick : function(e)
14545     {
14546         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14547             this.onTickableFooterButtonClick(e, false, false);
14548             return;
14549         }
14550         
14551         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14552             return;
14553         }
14554         
14555         this.page = 0;
14556         this.loadNext = false;
14557         this.hasFocus = true;
14558         
14559         if(this.triggerAction == 'all') {
14560             this.doQuery(this.allQuery, true);
14561         } else {
14562             this.doQuery(this.getRawValue());
14563         }
14564     },
14565     
14566     listKeyPress : function(e)
14567     {
14568         //Roo.log('listkeypress');
14569         // scroll to first matching element based on key pres..
14570         if (e.isSpecialKey()) {
14571             return false;
14572         }
14573         var k = String.fromCharCode(e.getKey()).toUpperCase();
14574         //Roo.log(k);
14575         var match  = false;
14576         var csel = this.view.getSelectedNodes();
14577         var cselitem = false;
14578         if (csel.length) {
14579             var ix = this.view.indexOf(csel[0]);
14580             cselitem  = this.store.getAt(ix);
14581             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14582                 cselitem = false;
14583             }
14584             
14585         }
14586         
14587         this.store.each(function(v) { 
14588             if (cselitem) {
14589                 // start at existing selection.
14590                 if (cselitem.id == v.id) {
14591                     cselitem = false;
14592                 }
14593                 return true;
14594             }
14595                 
14596             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14597                 match = this.store.indexOf(v);
14598                 return false;
14599             }
14600             return true;
14601         }, this);
14602         
14603         if (match === false) {
14604             return true; // no more action?
14605         }
14606         // scroll to?
14607         this.view.select(match);
14608         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14609         sn.scrollIntoView(sn.dom.parentNode, false);
14610     },
14611     
14612     onViewScroll : function(e, t){
14613         
14614         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){
14615             return;
14616         }
14617         
14618         this.hasQuery = true;
14619         
14620         this.loading = this.list.select('.loading', true).first();
14621         
14622         if(this.loading === null){
14623             this.list.createChild({
14624                 tag: 'div',
14625                 cls: 'loading roo-select2-more-results roo-select2-active',
14626                 html: 'Loading more results...'
14627             });
14628             
14629             this.loading = this.list.select('.loading', true).first();
14630             
14631             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14632             
14633             this.loading.hide();
14634         }
14635         
14636         this.loading.show();
14637         
14638         var _combo = this;
14639         
14640         this.page++;
14641         this.loadNext = true;
14642         
14643         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14644         
14645         return;
14646     },
14647     
14648     addItem : function(o)
14649     {   
14650         var dv = ''; // display value
14651         
14652         if (this.displayField) {
14653             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14654         } else {
14655             // this is an error condition!!!
14656             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14657         }
14658         
14659         if(!dv.length){
14660             return;
14661         }
14662         
14663         var choice = this.choices.createChild({
14664             tag: 'li',
14665             cls: 'roo-select2-search-choice',
14666             cn: [
14667                 {
14668                     tag: 'div',
14669                     html: dv
14670                 },
14671                 {
14672                     tag: 'a',
14673                     href: '#',
14674                     cls: 'roo-select2-search-choice-close fa fa-times',
14675                     tabindex: '-1'
14676                 }
14677             ]
14678             
14679         }, this.searchField);
14680         
14681         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14682         
14683         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14684         
14685         this.item.push(o);
14686         
14687         this.lastData = o;
14688         
14689         this.syncValue();
14690         
14691         this.inputEl().dom.value = '';
14692         
14693         this.validate();
14694     },
14695     
14696     onRemoveItem : function(e, _self, o)
14697     {
14698         e.preventDefault();
14699         
14700         this.lastItem = Roo.apply([], this.item);
14701         
14702         var index = this.item.indexOf(o.data) * 1;
14703         
14704         if( index < 0){
14705             Roo.log('not this item?!');
14706             return;
14707         }
14708         
14709         this.item.splice(index, 1);
14710         o.item.remove();
14711         
14712         this.syncValue();
14713         
14714         this.fireEvent('remove', this, e);
14715         
14716         this.validate();
14717         
14718     },
14719     
14720     syncValue : function()
14721     {
14722         if(!this.item.length){
14723             this.clearValue();
14724             return;
14725         }
14726             
14727         var value = [];
14728         var _this = this;
14729         Roo.each(this.item, function(i){
14730             if(_this.valueField){
14731                 value.push(i[_this.valueField]);
14732                 return;
14733             }
14734
14735             value.push(i);
14736         });
14737
14738         this.value = value.join(',');
14739
14740         if(this.hiddenField){
14741             this.hiddenField.dom.value = this.value;
14742         }
14743         
14744         this.store.fireEvent("datachanged", this.store);
14745         
14746         this.validate();
14747     },
14748     
14749     clearItem : function()
14750     {
14751         if(!this.multiple){
14752             return;
14753         }
14754         
14755         this.item = [];
14756         
14757         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14758            c.remove();
14759         });
14760         
14761         this.syncValue();
14762         
14763         this.validate();
14764         
14765         if(this.tickable && !Roo.isTouch){
14766             this.view.refresh();
14767         }
14768     },
14769     
14770     inputEl: function ()
14771     {
14772         if(Roo.isIOS && this.useNativeIOS){
14773             return this.el.select('select.roo-ios-select', true).first();
14774         }
14775         
14776         if(Roo.isTouch && this.mobileTouchView){
14777             return this.el.select('input.form-control',true).first();
14778         }
14779         
14780         if(this.tickable){
14781             return this.searchField;
14782         }
14783         
14784         return this.el.select('input.form-control',true).first();
14785     },
14786     
14787     onTickableFooterButtonClick : function(e, btn, el)
14788     {
14789         e.preventDefault();
14790         
14791         this.lastItem = Roo.apply([], this.item);
14792         
14793         if(btn && btn.name == 'cancel'){
14794             this.tickItems = Roo.apply([], this.item);
14795             this.collapse();
14796             return;
14797         }
14798         
14799         this.clearItem();
14800         
14801         var _this = this;
14802         
14803         Roo.each(this.tickItems, function(o){
14804             _this.addItem(o);
14805         });
14806         
14807         this.collapse();
14808         
14809     },
14810     
14811     validate : function()
14812     {
14813         if(this.getVisibilityEl().hasClass('hidden')){
14814             return true;
14815         }
14816         
14817         var v = this.getRawValue();
14818         
14819         if(this.multiple){
14820             v = this.getValue();
14821         }
14822         
14823         if(this.disabled || this.allowBlank || v.length){
14824             this.markValid();
14825             return true;
14826         }
14827         
14828         this.markInvalid();
14829         return false;
14830     },
14831     
14832     tickableInputEl : function()
14833     {
14834         if(!this.tickable || !this.editable){
14835             return this.inputEl();
14836         }
14837         
14838         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14839     },
14840     
14841     
14842     getAutoCreateTouchView : function()
14843     {
14844         var id = Roo.id();
14845         
14846         var cfg = {
14847             cls: 'form-group' //input-group
14848         };
14849         
14850         var input =  {
14851             tag: 'input',
14852             id : id,
14853             type : this.inputType,
14854             cls : 'form-control x-combo-noedit',
14855             autocomplete: 'new-password',
14856             placeholder : this.placeholder || '',
14857             readonly : true
14858         };
14859         
14860         if (this.name) {
14861             input.name = this.name;
14862         }
14863         
14864         if (this.size) {
14865             input.cls += ' input-' + this.size;
14866         }
14867         
14868         if (this.disabled) {
14869             input.disabled = true;
14870         }
14871         
14872         var inputblock = {
14873             cls : '',
14874             cn : [
14875                 input
14876             ]
14877         };
14878         
14879         if(this.before){
14880             inputblock.cls += ' input-group';
14881             
14882             inputblock.cn.unshift({
14883                 tag :'span',
14884                 cls : 'input-group-addon',
14885                 html : this.before
14886             });
14887         }
14888         
14889         if(this.removable && !this.multiple){
14890             inputblock.cls += ' roo-removable';
14891             
14892             inputblock.cn.push({
14893                 tag: 'button',
14894                 html : 'x',
14895                 cls : 'roo-combo-removable-btn close'
14896             });
14897         }
14898
14899         if(this.hasFeedback && !this.allowBlank){
14900             
14901             inputblock.cls += ' has-feedback';
14902             
14903             inputblock.cn.push({
14904                 tag: 'span',
14905                 cls: 'glyphicon form-control-feedback'
14906             });
14907             
14908         }
14909         
14910         if (this.after) {
14911             
14912             inputblock.cls += (this.before) ? '' : ' input-group';
14913             
14914             inputblock.cn.push({
14915                 tag :'span',
14916                 cls : 'input-group-addon',
14917                 html : this.after
14918             });
14919         }
14920
14921         var box = {
14922             tag: 'div',
14923             cn: [
14924                 {
14925                     tag: 'input',
14926                     type : 'hidden',
14927                     cls: 'form-hidden-field'
14928                 },
14929                 inputblock
14930             ]
14931             
14932         };
14933         
14934         if(this.multiple){
14935             box = {
14936                 tag: 'div',
14937                 cn: [
14938                     {
14939                         tag: 'input',
14940                         type : 'hidden',
14941                         cls: 'form-hidden-field'
14942                     },
14943                     {
14944                         tag: 'ul',
14945                         cls: 'roo-select2-choices',
14946                         cn:[
14947                             {
14948                                 tag: 'li',
14949                                 cls: 'roo-select2-search-field',
14950                                 cn: [
14951
14952                                     inputblock
14953                                 ]
14954                             }
14955                         ]
14956                     }
14957                 ]
14958             }
14959         };
14960         
14961         var combobox = {
14962             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14963             cn: [
14964                 box
14965             ]
14966         };
14967         
14968         if(!this.multiple && this.showToggleBtn){
14969             
14970             var caret = {
14971                         tag: 'span',
14972                         cls: 'caret'
14973             };
14974             
14975             if (this.caret != false) {
14976                 caret = {
14977                      tag: 'i',
14978                      cls: 'fa fa-' + this.caret
14979                 };
14980                 
14981             }
14982             
14983             combobox.cn.push({
14984                 tag :'span',
14985                 cls : 'input-group-addon btn dropdown-toggle',
14986                 cn : [
14987                     caret,
14988                     {
14989                         tag: 'span',
14990                         cls: 'combobox-clear',
14991                         cn  : [
14992                             {
14993                                 tag : 'i',
14994                                 cls: 'icon-remove'
14995                             }
14996                         ]
14997                     }
14998                 ]
14999
15000             })
15001         }
15002         
15003         if(this.multiple){
15004             combobox.cls += ' roo-select2-container-multi';
15005         }
15006         
15007         var align = this.labelAlign || this.parentLabelAlign();
15008         
15009         if (align ==='left' && this.fieldLabel.length) {
15010
15011             cfg.cn = [
15012                 {
15013                    tag : 'i',
15014                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15015                    tooltip : 'This field is required'
15016                 },
15017                 {
15018                     tag: 'label',
15019                     cls : 'control-label',
15020                     html : this.fieldLabel
15021
15022                 },
15023                 {
15024                     cls : '', 
15025                     cn: [
15026                         combobox
15027                     ]
15028                 }
15029             ];
15030             
15031             var labelCfg = cfg.cn[1];
15032             var contentCfg = cfg.cn[2];
15033             
15034
15035             if(this.indicatorpos == 'right'){
15036                 cfg.cn = [
15037                     {
15038                         tag: 'label',
15039                         'for' :  id,
15040                         cls : 'control-label',
15041                         cn : [
15042                             {
15043                                 tag : 'span',
15044                                 html : this.fieldLabel
15045                             },
15046                             {
15047                                 tag : 'i',
15048                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15049                                 tooltip : 'This field is required'
15050                             }
15051                         ]
15052                     },
15053                     {
15054                         cls : "",
15055                         cn: [
15056                             combobox
15057                         ]
15058                     }
15059
15060                 ];
15061                 
15062                 labelCfg = cfg.cn[0];
15063                 contentCfg = cfg.cn[1];
15064             }
15065             
15066            
15067             
15068             if(this.labelWidth > 12){
15069                 labelCfg.style = "width: " + this.labelWidth + 'px';
15070             }
15071             
15072             if(this.labelWidth < 13 && this.labelmd == 0){
15073                 this.labelmd = this.labelWidth;
15074             }
15075             
15076             if(this.labellg > 0){
15077                 labelCfg.cls += ' col-lg-' + this.labellg;
15078                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15079             }
15080             
15081             if(this.labelmd > 0){
15082                 labelCfg.cls += ' col-md-' + this.labelmd;
15083                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15084             }
15085             
15086             if(this.labelsm > 0){
15087                 labelCfg.cls += ' col-sm-' + this.labelsm;
15088                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15089             }
15090             
15091             if(this.labelxs > 0){
15092                 labelCfg.cls += ' col-xs-' + this.labelxs;
15093                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15094             }
15095                 
15096                 
15097         } else if ( this.fieldLabel.length) {
15098             cfg.cn = [
15099                 {
15100                    tag : 'i',
15101                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15102                    tooltip : 'This field is required'
15103                 },
15104                 {
15105                     tag: 'label',
15106                     cls : 'control-label',
15107                     html : this.fieldLabel
15108
15109                 },
15110                 {
15111                     cls : '', 
15112                     cn: [
15113                         combobox
15114                     ]
15115                 }
15116             ];
15117             
15118             if(this.indicatorpos == 'right'){
15119                 cfg.cn = [
15120                     {
15121                         tag: 'label',
15122                         cls : 'control-label',
15123                         html : this.fieldLabel,
15124                         cn : [
15125                             {
15126                                tag : 'i',
15127                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15128                                tooltip : 'This field is required'
15129                             }
15130                         ]
15131                     },
15132                     {
15133                         cls : '', 
15134                         cn: [
15135                             combobox
15136                         ]
15137                     }
15138                 ];
15139             }
15140         } else {
15141             cfg.cn = combobox;    
15142         }
15143         
15144         
15145         var settings = this;
15146         
15147         ['xs','sm','md','lg'].map(function(size){
15148             if (settings[size]) {
15149                 cfg.cls += ' col-' + size + '-' + settings[size];
15150             }
15151         });
15152         
15153         return cfg;
15154     },
15155     
15156     initTouchView : function()
15157     {
15158         this.renderTouchView();
15159         
15160         this.touchViewEl.on('scroll', function(){
15161             this.el.dom.scrollTop = 0;
15162         }, this);
15163         
15164         this.originalValue = this.getValue();
15165         
15166         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15167         
15168         this.inputEl().on("click", this.showTouchView, this);
15169         if (this.triggerEl) {
15170             this.triggerEl.on("click", this.showTouchView, this);
15171         }
15172         
15173         
15174         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15175         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15176         
15177         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15178         
15179         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15180         this.store.on('load', this.onTouchViewLoad, this);
15181         this.store.on('loadexception', this.onTouchViewLoadException, this);
15182         
15183         if(this.hiddenName){
15184             
15185             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15186             
15187             this.hiddenField.dom.value =
15188                 this.hiddenValue !== undefined ? this.hiddenValue :
15189                 this.value !== undefined ? this.value : '';
15190         
15191             this.el.dom.removeAttribute('name');
15192             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15193         }
15194         
15195         if(this.multiple){
15196             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15197             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15198         }
15199         
15200         if(this.removable && !this.multiple){
15201             var close = this.closeTriggerEl();
15202             if(close){
15203                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15204                 close.on('click', this.removeBtnClick, this, close);
15205             }
15206         }
15207         /*
15208          * fix the bug in Safari iOS8
15209          */
15210         this.inputEl().on("focus", function(e){
15211             document.activeElement.blur();
15212         }, this);
15213         
15214         return;
15215         
15216         
15217     },
15218     
15219     renderTouchView : function()
15220     {
15221         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15222         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15223         
15224         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15225         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15226         
15227         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15228         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15229         this.touchViewBodyEl.setStyle('overflow', 'auto');
15230         
15231         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15232         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15233         
15234         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15235         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15236         
15237     },
15238     
15239     showTouchView : function()
15240     {
15241         if(this.disabled){
15242             return;
15243         }
15244         
15245         this.touchViewHeaderEl.hide();
15246
15247         if(this.modalTitle.length){
15248             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15249             this.touchViewHeaderEl.show();
15250         }
15251
15252         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15253         this.touchViewEl.show();
15254
15255         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15256         
15257         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15258         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15259
15260         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15261
15262         if(this.modalTitle.length){
15263             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15264         }
15265         
15266         this.touchViewBodyEl.setHeight(bodyHeight);
15267
15268         if(this.animate){
15269             var _this = this;
15270             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15271         }else{
15272             this.touchViewEl.addClass('in');
15273         }
15274
15275         this.doTouchViewQuery();
15276         
15277     },
15278     
15279     hideTouchView : function()
15280     {
15281         this.touchViewEl.removeClass('in');
15282
15283         if(this.animate){
15284             var _this = this;
15285             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15286         }else{
15287             this.touchViewEl.setStyle('display', 'none');
15288         }
15289         
15290     },
15291     
15292     setTouchViewValue : function()
15293     {
15294         if(this.multiple){
15295             this.clearItem();
15296         
15297             var _this = this;
15298
15299             Roo.each(this.tickItems, function(o){
15300                 this.addItem(o);
15301             }, this);
15302         }
15303         
15304         this.hideTouchView();
15305     },
15306     
15307     doTouchViewQuery : function()
15308     {
15309         var qe = {
15310             query: '',
15311             forceAll: true,
15312             combo: this,
15313             cancel:false
15314         };
15315         
15316         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15317             return false;
15318         }
15319         
15320         if(!this.alwaysQuery || this.mode == 'local'){
15321             this.onTouchViewLoad();
15322             return;
15323         }
15324         
15325         this.store.load();
15326     },
15327     
15328     onTouchViewBeforeLoad : function(combo,opts)
15329     {
15330         return;
15331     },
15332
15333     // private
15334     onTouchViewLoad : function()
15335     {
15336         if(this.store.getCount() < 1){
15337             this.onTouchViewEmptyResults();
15338             return;
15339         }
15340         
15341         this.clearTouchView();
15342         
15343         var rawValue = this.getRawValue();
15344         
15345         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15346         
15347         this.tickItems = [];
15348         
15349         this.store.data.each(function(d, rowIndex){
15350             var row = this.touchViewListGroup.createChild(template);
15351             
15352             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15353                 row.addClass(d.data.cls);
15354             }
15355             
15356             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15357                 var cfg = {
15358                     data : d.data,
15359                     html : d.data[this.displayField]
15360                 };
15361                 
15362                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15363                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15364                 }
15365             }
15366             row.removeClass('selected');
15367             if(!this.multiple && this.valueField &&
15368                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15369             {
15370                 // radio buttons..
15371                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15372                 row.addClass('selected');
15373             }
15374             
15375             if(this.multiple && this.valueField &&
15376                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15377             {
15378                 
15379                 // checkboxes...
15380                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15381                 this.tickItems.push(d.data);
15382             }
15383             
15384             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15385             
15386         }, this);
15387         
15388         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15389         
15390         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15391
15392         if(this.modalTitle.length){
15393             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15394         }
15395
15396         var listHeight = this.touchViewListGroup.getHeight();
15397         
15398         var _this = this;
15399         
15400         if(firstChecked && listHeight > bodyHeight){
15401             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15402         }
15403         
15404     },
15405     
15406     onTouchViewLoadException : function()
15407     {
15408         this.hideTouchView();
15409     },
15410     
15411     onTouchViewEmptyResults : function()
15412     {
15413         this.clearTouchView();
15414         
15415         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15416         
15417         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15418         
15419     },
15420     
15421     clearTouchView : function()
15422     {
15423         this.touchViewListGroup.dom.innerHTML = '';
15424     },
15425     
15426     onTouchViewClick : function(e, el, o)
15427     {
15428         e.preventDefault();
15429         
15430         var row = o.row;
15431         var rowIndex = o.rowIndex;
15432         
15433         var r = this.store.getAt(rowIndex);
15434         
15435         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15436             
15437             if(!this.multiple){
15438                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15439                     c.dom.removeAttribute('checked');
15440                 }, this);
15441
15442                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15443
15444                 this.setFromData(r.data);
15445
15446                 var close = this.closeTriggerEl();
15447
15448                 if(close){
15449                     close.show();
15450                 }
15451
15452                 this.hideTouchView();
15453
15454                 this.fireEvent('select', this, r, rowIndex);
15455
15456                 return;
15457             }
15458
15459             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15460                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15461                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15462                 return;
15463             }
15464
15465             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15466             this.addItem(r.data);
15467             this.tickItems.push(r.data);
15468         }
15469     },
15470     
15471     getAutoCreateNativeIOS : function()
15472     {
15473         var cfg = {
15474             cls: 'form-group' //input-group,
15475         };
15476         
15477         var combobox =  {
15478             tag: 'select',
15479             cls : 'roo-ios-select'
15480         };
15481         
15482         if (this.name) {
15483             combobox.name = this.name;
15484         }
15485         
15486         if (this.disabled) {
15487             combobox.disabled = true;
15488         }
15489         
15490         var settings = this;
15491         
15492         ['xs','sm','md','lg'].map(function(size){
15493             if (settings[size]) {
15494                 cfg.cls += ' col-' + size + '-' + settings[size];
15495             }
15496         });
15497         
15498         cfg.cn = combobox;
15499         
15500         return cfg;
15501         
15502     },
15503     
15504     initIOSView : function()
15505     {
15506         this.store.on('load', this.onIOSViewLoad, this);
15507         
15508         return;
15509     },
15510     
15511     onIOSViewLoad : function()
15512     {
15513         if(this.store.getCount() < 1){
15514             return;
15515         }
15516         
15517         this.clearIOSView();
15518         
15519         if(this.allowBlank) {
15520             
15521             var default_text = '-- SELECT --';
15522             
15523             if(this.placeholder.length){
15524                 default_text = this.placeholder;
15525             }
15526             
15527             if(this.emptyTitle.length){
15528                 default_text += ' - ' + this.emptyTitle + ' -';
15529             }
15530             
15531             var opt = this.inputEl().createChild({
15532                 tag: 'option',
15533                 value : 0,
15534                 html : default_text
15535             });
15536             
15537             var o = {};
15538             o[this.valueField] = 0;
15539             o[this.displayField] = default_text;
15540             
15541             this.ios_options.push({
15542                 data : o,
15543                 el : opt
15544             });
15545             
15546         }
15547         
15548         this.store.data.each(function(d, rowIndex){
15549             
15550             var html = '';
15551             
15552             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15553                 html = d.data[this.displayField];
15554             }
15555             
15556             var value = '';
15557             
15558             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15559                 value = d.data[this.valueField];
15560             }
15561             
15562             var option = {
15563                 tag: 'option',
15564                 value : value,
15565                 html : html
15566             };
15567             
15568             if(this.value == d.data[this.valueField]){
15569                 option['selected'] = true;
15570             }
15571             
15572             var opt = this.inputEl().createChild(option);
15573             
15574             this.ios_options.push({
15575                 data : d.data,
15576                 el : opt
15577             });
15578             
15579         }, this);
15580         
15581         this.inputEl().on('change', function(){
15582            this.fireEvent('select', this);
15583         }, this);
15584         
15585     },
15586     
15587     clearIOSView: function()
15588     {
15589         this.inputEl().dom.innerHTML = '';
15590         
15591         this.ios_options = [];
15592     },
15593     
15594     setIOSValue: function(v)
15595     {
15596         this.value = v;
15597         
15598         if(!this.ios_options){
15599             return;
15600         }
15601         
15602         Roo.each(this.ios_options, function(opts){
15603            
15604            opts.el.dom.removeAttribute('selected');
15605            
15606            if(opts.data[this.valueField] != v){
15607                return;
15608            }
15609            
15610            opts.el.dom.setAttribute('selected', true);
15611            
15612         }, this);
15613     }
15614
15615     /** 
15616     * @cfg {Boolean} grow 
15617     * @hide 
15618     */
15619     /** 
15620     * @cfg {Number} growMin 
15621     * @hide 
15622     */
15623     /** 
15624     * @cfg {Number} growMax 
15625     * @hide 
15626     */
15627     /**
15628      * @hide
15629      * @method autoSize
15630      */
15631 });
15632
15633 Roo.apply(Roo.bootstrap.ComboBox,  {
15634     
15635     header : {
15636         tag: 'div',
15637         cls: 'modal-header',
15638         cn: [
15639             {
15640                 tag: 'h4',
15641                 cls: 'modal-title'
15642             }
15643         ]
15644     },
15645     
15646     body : {
15647         tag: 'div',
15648         cls: 'modal-body',
15649         cn: [
15650             {
15651                 tag: 'ul',
15652                 cls: 'list-group'
15653             }
15654         ]
15655     },
15656     
15657     listItemRadio : {
15658         tag: 'li',
15659         cls: 'list-group-item',
15660         cn: [
15661             {
15662                 tag: 'span',
15663                 cls: 'roo-combobox-list-group-item-value'
15664             },
15665             {
15666                 tag: 'div',
15667                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15668                 cn: [
15669                     {
15670                         tag: 'input',
15671                         type: 'radio'
15672                     },
15673                     {
15674                         tag: 'label'
15675                     }
15676                 ]
15677             }
15678         ]
15679     },
15680     
15681     listItemCheckbox : {
15682         tag: 'li',
15683         cls: 'list-group-item',
15684         cn: [
15685             {
15686                 tag: 'span',
15687                 cls: 'roo-combobox-list-group-item-value'
15688             },
15689             {
15690                 tag: 'div',
15691                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15692                 cn: [
15693                     {
15694                         tag: 'input',
15695                         type: 'checkbox'
15696                     },
15697                     {
15698                         tag: 'label'
15699                     }
15700                 ]
15701             }
15702         ]
15703     },
15704     
15705     emptyResult : {
15706         tag: 'div',
15707         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15708     },
15709     
15710     footer : {
15711         tag: 'div',
15712         cls: 'modal-footer',
15713         cn: [
15714             {
15715                 tag: 'div',
15716                 cls: 'row',
15717                 cn: [
15718                     {
15719                         tag: 'div',
15720                         cls: 'col-xs-6 text-left',
15721                         cn: {
15722                             tag: 'button',
15723                             cls: 'btn btn-danger roo-touch-view-cancel',
15724                             html: 'Cancel'
15725                         }
15726                     },
15727                     {
15728                         tag: 'div',
15729                         cls: 'col-xs-6 text-right',
15730                         cn: {
15731                             tag: 'button',
15732                             cls: 'btn btn-success roo-touch-view-ok',
15733                             html: 'OK'
15734                         }
15735                     }
15736                 ]
15737             }
15738         ]
15739         
15740     }
15741 });
15742
15743 Roo.apply(Roo.bootstrap.ComboBox,  {
15744     
15745     touchViewTemplate : {
15746         tag: 'div',
15747         cls: 'modal fade roo-combobox-touch-view',
15748         cn: [
15749             {
15750                 tag: 'div',
15751                 cls: 'modal-dialog',
15752                 style : 'position:fixed', // we have to fix position....
15753                 cn: [
15754                     {
15755                         tag: 'div',
15756                         cls: 'modal-content',
15757                         cn: [
15758                             Roo.bootstrap.ComboBox.header,
15759                             Roo.bootstrap.ComboBox.body,
15760                             Roo.bootstrap.ComboBox.footer
15761                         ]
15762                     }
15763                 ]
15764             }
15765         ]
15766     }
15767 });/*
15768  * Based on:
15769  * Ext JS Library 1.1.1
15770  * Copyright(c) 2006-2007, Ext JS, LLC.
15771  *
15772  * Originally Released Under LGPL - original licence link has changed is not relivant.
15773  *
15774  * Fork - LGPL
15775  * <script type="text/javascript">
15776  */
15777
15778 /**
15779  * @class Roo.View
15780  * @extends Roo.util.Observable
15781  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15782  * This class also supports single and multi selection modes. <br>
15783  * Create a data model bound view:
15784  <pre><code>
15785  var store = new Roo.data.Store(...);
15786
15787  var view = new Roo.View({
15788     el : "my-element",
15789     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15790  
15791     singleSelect: true,
15792     selectedClass: "ydataview-selected",
15793     store: store
15794  });
15795
15796  // listen for node click?
15797  view.on("click", function(vw, index, node, e){
15798  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15799  });
15800
15801  // load XML data
15802  dataModel.load("foobar.xml");
15803  </code></pre>
15804  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15805  * <br><br>
15806  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15807  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15808  * 
15809  * Note: old style constructor is still suported (container, template, config)
15810  * 
15811  * @constructor
15812  * Create a new View
15813  * @param {Object} config The config object
15814  * 
15815  */
15816 Roo.View = function(config, depreciated_tpl, depreciated_config){
15817     
15818     this.parent = false;
15819     
15820     if (typeof(depreciated_tpl) == 'undefined') {
15821         // new way.. - universal constructor.
15822         Roo.apply(this, config);
15823         this.el  = Roo.get(this.el);
15824     } else {
15825         // old format..
15826         this.el  = Roo.get(config);
15827         this.tpl = depreciated_tpl;
15828         Roo.apply(this, depreciated_config);
15829     }
15830     this.wrapEl  = this.el.wrap().wrap();
15831     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15832     
15833     
15834     if(typeof(this.tpl) == "string"){
15835         this.tpl = new Roo.Template(this.tpl);
15836     } else {
15837         // support xtype ctors..
15838         this.tpl = new Roo.factory(this.tpl, Roo);
15839     }
15840     
15841     
15842     this.tpl.compile();
15843     
15844     /** @private */
15845     this.addEvents({
15846         /**
15847          * @event beforeclick
15848          * Fires before a click is processed. Returns false to cancel the default action.
15849          * @param {Roo.View} this
15850          * @param {Number} index The index of the target node
15851          * @param {HTMLElement} node The target node
15852          * @param {Roo.EventObject} e The raw event object
15853          */
15854             "beforeclick" : true,
15855         /**
15856          * @event click
15857          * Fires when a template node is clicked.
15858          * @param {Roo.View} this
15859          * @param {Number} index The index of the target node
15860          * @param {HTMLElement} node The target node
15861          * @param {Roo.EventObject} e The raw event object
15862          */
15863             "click" : true,
15864         /**
15865          * @event dblclick
15866          * Fires when a template node is double clicked.
15867          * @param {Roo.View} this
15868          * @param {Number} index The index of the target node
15869          * @param {HTMLElement} node The target node
15870          * @param {Roo.EventObject} e The raw event object
15871          */
15872             "dblclick" : true,
15873         /**
15874          * @event contextmenu
15875          * Fires when a template node is right clicked.
15876          * @param {Roo.View} this
15877          * @param {Number} index The index of the target node
15878          * @param {HTMLElement} node The target node
15879          * @param {Roo.EventObject} e The raw event object
15880          */
15881             "contextmenu" : true,
15882         /**
15883          * @event selectionchange
15884          * Fires when the selected nodes change.
15885          * @param {Roo.View} this
15886          * @param {Array} selections Array of the selected nodes
15887          */
15888             "selectionchange" : true,
15889     
15890         /**
15891          * @event beforeselect
15892          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15893          * @param {Roo.View} this
15894          * @param {HTMLElement} node The node to be selected
15895          * @param {Array} selections Array of currently selected nodes
15896          */
15897             "beforeselect" : true,
15898         /**
15899          * @event preparedata
15900          * Fires on every row to render, to allow you to change the data.
15901          * @param {Roo.View} this
15902          * @param {Object} data to be rendered (change this)
15903          */
15904           "preparedata" : true
15905           
15906           
15907         });
15908
15909
15910
15911     this.el.on({
15912         "click": this.onClick,
15913         "dblclick": this.onDblClick,
15914         "contextmenu": this.onContextMenu,
15915         scope:this
15916     });
15917
15918     this.selections = [];
15919     this.nodes = [];
15920     this.cmp = new Roo.CompositeElementLite([]);
15921     if(this.store){
15922         this.store = Roo.factory(this.store, Roo.data);
15923         this.setStore(this.store, true);
15924     }
15925     
15926     if ( this.footer && this.footer.xtype) {
15927            
15928          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15929         
15930         this.footer.dataSource = this.store;
15931         this.footer.container = fctr;
15932         this.footer = Roo.factory(this.footer, Roo);
15933         fctr.insertFirst(this.el);
15934         
15935         // this is a bit insane - as the paging toolbar seems to detach the el..
15936 //        dom.parentNode.parentNode.parentNode
15937          // they get detached?
15938     }
15939     
15940     
15941     Roo.View.superclass.constructor.call(this);
15942     
15943     
15944 };
15945
15946 Roo.extend(Roo.View, Roo.util.Observable, {
15947     
15948      /**
15949      * @cfg {Roo.data.Store} store Data store to load data from.
15950      */
15951     store : false,
15952     
15953     /**
15954      * @cfg {String|Roo.Element} el The container element.
15955      */
15956     el : '',
15957     
15958     /**
15959      * @cfg {String|Roo.Template} tpl The template used by this View 
15960      */
15961     tpl : false,
15962     /**
15963      * @cfg {String} dataName the named area of the template to use as the data area
15964      *                          Works with domtemplates roo-name="name"
15965      */
15966     dataName: false,
15967     /**
15968      * @cfg {String} selectedClass The css class to add to selected nodes
15969      */
15970     selectedClass : "x-view-selected",
15971      /**
15972      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15973      */
15974     emptyText : "",
15975     
15976     /**
15977      * @cfg {String} text to display on mask (default Loading)
15978      */
15979     mask : false,
15980     /**
15981      * @cfg {Boolean} multiSelect Allow multiple selection
15982      */
15983     multiSelect : false,
15984     /**
15985      * @cfg {Boolean} singleSelect Allow single selection
15986      */
15987     singleSelect:  false,
15988     
15989     /**
15990      * @cfg {Boolean} toggleSelect - selecting 
15991      */
15992     toggleSelect : false,
15993     
15994     /**
15995      * @cfg {Boolean} tickable - selecting 
15996      */
15997     tickable : false,
15998     
15999     /**
16000      * Returns the element this view is bound to.
16001      * @return {Roo.Element}
16002      */
16003     getEl : function(){
16004         return this.wrapEl;
16005     },
16006     
16007     
16008
16009     /**
16010      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16011      */
16012     refresh : function(){
16013         //Roo.log('refresh');
16014         var t = this.tpl;
16015         
16016         // if we are using something like 'domtemplate', then
16017         // the what gets used is:
16018         // t.applySubtemplate(NAME, data, wrapping data..)
16019         // the outer template then get' applied with
16020         //     the store 'extra data'
16021         // and the body get's added to the
16022         //      roo-name="data" node?
16023         //      <span class='roo-tpl-{name}'></span> ?????
16024         
16025         
16026         
16027         this.clearSelections();
16028         this.el.update("");
16029         var html = [];
16030         var records = this.store.getRange();
16031         if(records.length < 1) {
16032             
16033             // is this valid??  = should it render a template??
16034             
16035             this.el.update(this.emptyText);
16036             return;
16037         }
16038         var el = this.el;
16039         if (this.dataName) {
16040             this.el.update(t.apply(this.store.meta)); //????
16041             el = this.el.child('.roo-tpl-' + this.dataName);
16042         }
16043         
16044         for(var i = 0, len = records.length; i < len; i++){
16045             var data = this.prepareData(records[i].data, i, records[i]);
16046             this.fireEvent("preparedata", this, data, i, records[i]);
16047             
16048             var d = Roo.apply({}, data);
16049             
16050             if(this.tickable){
16051                 Roo.apply(d, {'roo-id' : Roo.id()});
16052                 
16053                 var _this = this;
16054             
16055                 Roo.each(this.parent.item, function(item){
16056                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16057                         return;
16058                     }
16059                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16060                 });
16061             }
16062             
16063             html[html.length] = Roo.util.Format.trim(
16064                 this.dataName ?
16065                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16066                     t.apply(d)
16067             );
16068         }
16069         
16070         
16071         
16072         el.update(html.join(""));
16073         this.nodes = el.dom.childNodes;
16074         this.updateIndexes(0);
16075     },
16076     
16077
16078     /**
16079      * Function to override to reformat the data that is sent to
16080      * the template for each node.
16081      * DEPRICATED - use the preparedata event handler.
16082      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16083      * a JSON object for an UpdateManager bound view).
16084      */
16085     prepareData : function(data, index, record)
16086     {
16087         this.fireEvent("preparedata", this, data, index, record);
16088         return data;
16089     },
16090
16091     onUpdate : function(ds, record){
16092         // Roo.log('on update');   
16093         this.clearSelections();
16094         var index = this.store.indexOf(record);
16095         var n = this.nodes[index];
16096         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16097         n.parentNode.removeChild(n);
16098         this.updateIndexes(index, index);
16099     },
16100
16101     
16102     
16103 // --------- FIXME     
16104     onAdd : function(ds, records, index)
16105     {
16106         //Roo.log(['on Add', ds, records, index] );        
16107         this.clearSelections();
16108         if(this.nodes.length == 0){
16109             this.refresh();
16110             return;
16111         }
16112         var n = this.nodes[index];
16113         for(var i = 0, len = records.length; i < len; i++){
16114             var d = this.prepareData(records[i].data, i, records[i]);
16115             if(n){
16116                 this.tpl.insertBefore(n, d);
16117             }else{
16118                 
16119                 this.tpl.append(this.el, d);
16120             }
16121         }
16122         this.updateIndexes(index);
16123     },
16124
16125     onRemove : function(ds, record, index){
16126        // Roo.log('onRemove');
16127         this.clearSelections();
16128         var el = this.dataName  ?
16129             this.el.child('.roo-tpl-' + this.dataName) :
16130             this.el; 
16131         
16132         el.dom.removeChild(this.nodes[index]);
16133         this.updateIndexes(index);
16134     },
16135
16136     /**
16137      * Refresh an individual node.
16138      * @param {Number} index
16139      */
16140     refreshNode : function(index){
16141         this.onUpdate(this.store, this.store.getAt(index));
16142     },
16143
16144     updateIndexes : function(startIndex, endIndex){
16145         var ns = this.nodes;
16146         startIndex = startIndex || 0;
16147         endIndex = endIndex || ns.length - 1;
16148         for(var i = startIndex; i <= endIndex; i++){
16149             ns[i].nodeIndex = i;
16150         }
16151     },
16152
16153     /**
16154      * Changes the data store this view uses and refresh the view.
16155      * @param {Store} store
16156      */
16157     setStore : function(store, initial){
16158         if(!initial && this.store){
16159             this.store.un("datachanged", this.refresh);
16160             this.store.un("add", this.onAdd);
16161             this.store.un("remove", this.onRemove);
16162             this.store.un("update", this.onUpdate);
16163             this.store.un("clear", this.refresh);
16164             this.store.un("beforeload", this.onBeforeLoad);
16165             this.store.un("load", this.onLoad);
16166             this.store.un("loadexception", this.onLoad);
16167         }
16168         if(store){
16169           
16170             store.on("datachanged", this.refresh, this);
16171             store.on("add", this.onAdd, this);
16172             store.on("remove", this.onRemove, this);
16173             store.on("update", this.onUpdate, this);
16174             store.on("clear", this.refresh, this);
16175             store.on("beforeload", this.onBeforeLoad, this);
16176             store.on("load", this.onLoad, this);
16177             store.on("loadexception", this.onLoad, this);
16178         }
16179         
16180         if(store){
16181             this.refresh();
16182         }
16183     },
16184     /**
16185      * onbeforeLoad - masks the loading area.
16186      *
16187      */
16188     onBeforeLoad : function(store,opts)
16189     {
16190          //Roo.log('onBeforeLoad');   
16191         if (!opts.add) {
16192             this.el.update("");
16193         }
16194         this.el.mask(this.mask ? this.mask : "Loading" ); 
16195     },
16196     onLoad : function ()
16197     {
16198         this.el.unmask();
16199     },
16200     
16201
16202     /**
16203      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16204      * @param {HTMLElement} node
16205      * @return {HTMLElement} The template node
16206      */
16207     findItemFromChild : function(node){
16208         var el = this.dataName  ?
16209             this.el.child('.roo-tpl-' + this.dataName,true) :
16210             this.el.dom; 
16211         
16212         if(!node || node.parentNode == el){
16213                     return node;
16214             }
16215             var p = node.parentNode;
16216             while(p && p != el){
16217             if(p.parentNode == el){
16218                 return p;
16219             }
16220             p = p.parentNode;
16221         }
16222             return null;
16223     },
16224
16225     /** @ignore */
16226     onClick : function(e){
16227         var item = this.findItemFromChild(e.getTarget());
16228         if(item){
16229             var index = this.indexOf(item);
16230             if(this.onItemClick(item, index, e) !== false){
16231                 this.fireEvent("click", this, index, item, e);
16232             }
16233         }else{
16234             this.clearSelections();
16235         }
16236     },
16237
16238     /** @ignore */
16239     onContextMenu : function(e){
16240         var item = this.findItemFromChild(e.getTarget());
16241         if(item){
16242             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16243         }
16244     },
16245
16246     /** @ignore */
16247     onDblClick : function(e){
16248         var item = this.findItemFromChild(e.getTarget());
16249         if(item){
16250             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16251         }
16252     },
16253
16254     onItemClick : function(item, index, e)
16255     {
16256         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16257             return false;
16258         }
16259         if (this.toggleSelect) {
16260             var m = this.isSelected(item) ? 'unselect' : 'select';
16261             //Roo.log(m);
16262             var _t = this;
16263             _t[m](item, true, false);
16264             return true;
16265         }
16266         if(this.multiSelect || this.singleSelect){
16267             if(this.multiSelect && e.shiftKey && this.lastSelection){
16268                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16269             }else{
16270                 this.select(item, this.multiSelect && e.ctrlKey);
16271                 this.lastSelection = item;
16272             }
16273             
16274             if(!this.tickable){
16275                 e.preventDefault();
16276             }
16277             
16278         }
16279         return true;
16280     },
16281
16282     /**
16283      * Get the number of selected nodes.
16284      * @return {Number}
16285      */
16286     getSelectionCount : function(){
16287         return this.selections.length;
16288     },
16289
16290     /**
16291      * Get the currently selected nodes.
16292      * @return {Array} An array of HTMLElements
16293      */
16294     getSelectedNodes : function(){
16295         return this.selections;
16296     },
16297
16298     /**
16299      * Get the indexes of the selected nodes.
16300      * @return {Array}
16301      */
16302     getSelectedIndexes : function(){
16303         var indexes = [], s = this.selections;
16304         for(var i = 0, len = s.length; i < len; i++){
16305             indexes.push(s[i].nodeIndex);
16306         }
16307         return indexes;
16308     },
16309
16310     /**
16311      * Clear all selections
16312      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16313      */
16314     clearSelections : function(suppressEvent){
16315         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16316             this.cmp.elements = this.selections;
16317             this.cmp.removeClass(this.selectedClass);
16318             this.selections = [];
16319             if(!suppressEvent){
16320                 this.fireEvent("selectionchange", this, this.selections);
16321             }
16322         }
16323     },
16324
16325     /**
16326      * Returns true if the passed node is selected
16327      * @param {HTMLElement/Number} node The node or node index
16328      * @return {Boolean}
16329      */
16330     isSelected : function(node){
16331         var s = this.selections;
16332         if(s.length < 1){
16333             return false;
16334         }
16335         node = this.getNode(node);
16336         return s.indexOf(node) !== -1;
16337     },
16338
16339     /**
16340      * Selects nodes.
16341      * @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
16342      * @param {Boolean} keepExisting (optional) true to keep existing selections
16343      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16344      */
16345     select : function(nodeInfo, keepExisting, suppressEvent){
16346         if(nodeInfo instanceof Array){
16347             if(!keepExisting){
16348                 this.clearSelections(true);
16349             }
16350             for(var i = 0, len = nodeInfo.length; i < len; i++){
16351                 this.select(nodeInfo[i], true, true);
16352             }
16353             return;
16354         } 
16355         var node = this.getNode(nodeInfo);
16356         if(!node || this.isSelected(node)){
16357             return; // already selected.
16358         }
16359         if(!keepExisting){
16360             this.clearSelections(true);
16361         }
16362         
16363         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16364             Roo.fly(node).addClass(this.selectedClass);
16365             this.selections.push(node);
16366             if(!suppressEvent){
16367                 this.fireEvent("selectionchange", this, this.selections);
16368             }
16369         }
16370         
16371         
16372     },
16373       /**
16374      * Unselects nodes.
16375      * @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
16376      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16377      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16378      */
16379     unselect : function(nodeInfo, keepExisting, suppressEvent)
16380     {
16381         if(nodeInfo instanceof Array){
16382             Roo.each(this.selections, function(s) {
16383                 this.unselect(s, nodeInfo);
16384             }, this);
16385             return;
16386         }
16387         var node = this.getNode(nodeInfo);
16388         if(!node || !this.isSelected(node)){
16389             //Roo.log("not selected");
16390             return; // not selected.
16391         }
16392         // fireevent???
16393         var ns = [];
16394         Roo.each(this.selections, function(s) {
16395             if (s == node ) {
16396                 Roo.fly(node).removeClass(this.selectedClass);
16397
16398                 return;
16399             }
16400             ns.push(s);
16401         },this);
16402         
16403         this.selections= ns;
16404         this.fireEvent("selectionchange", this, this.selections);
16405     },
16406
16407     /**
16408      * Gets a template node.
16409      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16410      * @return {HTMLElement} The node or null if it wasn't found
16411      */
16412     getNode : function(nodeInfo){
16413         if(typeof nodeInfo == "string"){
16414             return document.getElementById(nodeInfo);
16415         }else if(typeof nodeInfo == "number"){
16416             return this.nodes[nodeInfo];
16417         }
16418         return nodeInfo;
16419     },
16420
16421     /**
16422      * Gets a range template nodes.
16423      * @param {Number} startIndex
16424      * @param {Number} endIndex
16425      * @return {Array} An array of nodes
16426      */
16427     getNodes : function(start, end){
16428         var ns = this.nodes;
16429         start = start || 0;
16430         end = typeof end == "undefined" ? ns.length - 1 : end;
16431         var nodes = [];
16432         if(start <= end){
16433             for(var i = start; i <= end; i++){
16434                 nodes.push(ns[i]);
16435             }
16436         } else{
16437             for(var i = start; i >= end; i--){
16438                 nodes.push(ns[i]);
16439             }
16440         }
16441         return nodes;
16442     },
16443
16444     /**
16445      * Finds the index of the passed node
16446      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16447      * @return {Number} The index of the node or -1
16448      */
16449     indexOf : function(node){
16450         node = this.getNode(node);
16451         if(typeof node.nodeIndex == "number"){
16452             return node.nodeIndex;
16453         }
16454         var ns = this.nodes;
16455         for(var i = 0, len = ns.length; i < len; i++){
16456             if(ns[i] == node){
16457                 return i;
16458             }
16459         }
16460         return -1;
16461     }
16462 });
16463 /*
16464  * - LGPL
16465  *
16466  * based on jquery fullcalendar
16467  * 
16468  */
16469
16470 Roo.bootstrap = Roo.bootstrap || {};
16471 /**
16472  * @class Roo.bootstrap.Calendar
16473  * @extends Roo.bootstrap.Component
16474  * Bootstrap Calendar class
16475  * @cfg {Boolean} loadMask (true|false) default false
16476  * @cfg {Object} header generate the user specific header of the calendar, default false
16477
16478  * @constructor
16479  * Create a new Container
16480  * @param {Object} config The config object
16481  */
16482
16483
16484
16485 Roo.bootstrap.Calendar = function(config){
16486     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16487      this.addEvents({
16488         /**
16489              * @event select
16490              * Fires when a date is selected
16491              * @param {DatePicker} this
16492              * @param {Date} date The selected date
16493              */
16494         'select': true,
16495         /**
16496              * @event monthchange
16497              * Fires when the displayed month changes 
16498              * @param {DatePicker} this
16499              * @param {Date} date The selected month
16500              */
16501         'monthchange': true,
16502         /**
16503              * @event evententer
16504              * Fires when mouse over an event
16505              * @param {Calendar} this
16506              * @param {event} Event
16507              */
16508         'evententer': true,
16509         /**
16510              * @event eventleave
16511              * Fires when the mouse leaves an
16512              * @param {Calendar} this
16513              * @param {event}
16514              */
16515         'eventleave': true,
16516         /**
16517              * @event eventclick
16518              * Fires when the mouse click an
16519              * @param {Calendar} this
16520              * @param {event}
16521              */
16522         'eventclick': true
16523         
16524     });
16525
16526 };
16527
16528 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16529     
16530      /**
16531      * @cfg {Number} startDay
16532      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16533      */
16534     startDay : 0,
16535     
16536     loadMask : false,
16537     
16538     header : false,
16539       
16540     getAutoCreate : function(){
16541         
16542         
16543         var fc_button = function(name, corner, style, content ) {
16544             return Roo.apply({},{
16545                 tag : 'span',
16546                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16547                          (corner.length ?
16548                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16549                             ''
16550                         ),
16551                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16552                 unselectable: 'on'
16553             });
16554         };
16555         
16556         var header = {};
16557         
16558         if(!this.header){
16559             header = {
16560                 tag : 'table',
16561                 cls : 'fc-header',
16562                 style : 'width:100%',
16563                 cn : [
16564                     {
16565                         tag: 'tr',
16566                         cn : [
16567                             {
16568                                 tag : 'td',
16569                                 cls : 'fc-header-left',
16570                                 cn : [
16571                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16572                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16573                                     { tag: 'span', cls: 'fc-header-space' },
16574                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16575
16576
16577                                 ]
16578                             },
16579
16580                             {
16581                                 tag : 'td',
16582                                 cls : 'fc-header-center',
16583                                 cn : [
16584                                     {
16585                                         tag: 'span',
16586                                         cls: 'fc-header-title',
16587                                         cn : {
16588                                             tag: 'H2',
16589                                             html : 'month / year'
16590                                         }
16591                                     }
16592
16593                                 ]
16594                             },
16595                             {
16596                                 tag : 'td',
16597                                 cls : 'fc-header-right',
16598                                 cn : [
16599                               /*      fc_button('month', 'left', '', 'month' ),
16600                                     fc_button('week', '', '', 'week' ),
16601                                     fc_button('day', 'right', '', 'day' )
16602                                 */    
16603
16604                                 ]
16605                             }
16606
16607                         ]
16608                     }
16609                 ]
16610             };
16611         }
16612         
16613         header = this.header;
16614         
16615        
16616         var cal_heads = function() {
16617             var ret = [];
16618             // fixme - handle this.
16619             
16620             for (var i =0; i < Date.dayNames.length; i++) {
16621                 var d = Date.dayNames[i];
16622                 ret.push({
16623                     tag: 'th',
16624                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16625                     html : d.substring(0,3)
16626                 });
16627                 
16628             }
16629             ret[0].cls += ' fc-first';
16630             ret[6].cls += ' fc-last';
16631             return ret;
16632         };
16633         var cal_cell = function(n) {
16634             return  {
16635                 tag: 'td',
16636                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16637                 cn : [
16638                     {
16639                         cn : [
16640                             {
16641                                 cls: 'fc-day-number',
16642                                 html: 'D'
16643                             },
16644                             {
16645                                 cls: 'fc-day-content',
16646                              
16647                                 cn : [
16648                                      {
16649                                         style: 'position: relative;' // height: 17px;
16650                                     }
16651                                 ]
16652                             }
16653                             
16654                             
16655                         ]
16656                     }
16657                 ]
16658                 
16659             }
16660         };
16661         var cal_rows = function() {
16662             
16663             var ret = [];
16664             for (var r = 0; r < 6; r++) {
16665                 var row= {
16666                     tag : 'tr',
16667                     cls : 'fc-week',
16668                     cn : []
16669                 };
16670                 
16671                 for (var i =0; i < Date.dayNames.length; i++) {
16672                     var d = Date.dayNames[i];
16673                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16674
16675                 }
16676                 row.cn[0].cls+=' fc-first';
16677                 row.cn[0].cn[0].style = 'min-height:90px';
16678                 row.cn[6].cls+=' fc-last';
16679                 ret.push(row);
16680                 
16681             }
16682             ret[0].cls += ' fc-first';
16683             ret[4].cls += ' fc-prev-last';
16684             ret[5].cls += ' fc-last';
16685             return ret;
16686             
16687         };
16688         
16689         var cal_table = {
16690             tag: 'table',
16691             cls: 'fc-border-separate',
16692             style : 'width:100%',
16693             cellspacing  : 0,
16694             cn : [
16695                 { 
16696                     tag: 'thead',
16697                     cn : [
16698                         { 
16699                             tag: 'tr',
16700                             cls : 'fc-first fc-last',
16701                             cn : cal_heads()
16702                         }
16703                     ]
16704                 },
16705                 { 
16706                     tag: 'tbody',
16707                     cn : cal_rows()
16708                 }
16709                   
16710             ]
16711         };
16712          
16713          var cfg = {
16714             cls : 'fc fc-ltr',
16715             cn : [
16716                 header,
16717                 {
16718                     cls : 'fc-content',
16719                     style : "position: relative;",
16720                     cn : [
16721                         {
16722                             cls : 'fc-view fc-view-month fc-grid',
16723                             style : 'position: relative',
16724                             unselectable : 'on',
16725                             cn : [
16726                                 {
16727                                     cls : 'fc-event-container',
16728                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16729                                 },
16730                                 cal_table
16731                             ]
16732                         }
16733                     ]
16734     
16735                 }
16736            ] 
16737             
16738         };
16739         
16740          
16741         
16742         return cfg;
16743     },
16744     
16745     
16746     initEvents : function()
16747     {
16748         if(!this.store){
16749             throw "can not find store for calendar";
16750         }
16751         
16752         var mark = {
16753             tag: "div",
16754             cls:"x-dlg-mask",
16755             style: "text-align:center",
16756             cn: [
16757                 {
16758                     tag: "div",
16759                     style: "background-color:white;width:50%;margin:250 auto",
16760                     cn: [
16761                         {
16762                             tag: "img",
16763                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16764                         },
16765                         {
16766                             tag: "span",
16767                             html: "Loading"
16768                         }
16769                         
16770                     ]
16771                 }
16772             ]
16773         };
16774         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16775         
16776         var size = this.el.select('.fc-content', true).first().getSize();
16777         this.maskEl.setSize(size.width, size.height);
16778         this.maskEl.enableDisplayMode("block");
16779         if(!this.loadMask){
16780             this.maskEl.hide();
16781         }
16782         
16783         this.store = Roo.factory(this.store, Roo.data);
16784         this.store.on('load', this.onLoad, this);
16785         this.store.on('beforeload', this.onBeforeLoad, this);
16786         
16787         this.resize();
16788         
16789         this.cells = this.el.select('.fc-day',true);
16790         //Roo.log(this.cells);
16791         this.textNodes = this.el.query('.fc-day-number');
16792         this.cells.addClassOnOver('fc-state-hover');
16793         
16794         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16795         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16796         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16797         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16798         
16799         this.on('monthchange', this.onMonthChange, this);
16800         
16801         this.update(new Date().clearTime());
16802     },
16803     
16804     resize : function() {
16805         var sz  = this.el.getSize();
16806         
16807         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16808         this.el.select('.fc-day-content div',true).setHeight(34);
16809     },
16810     
16811     
16812     // private
16813     showPrevMonth : function(e){
16814         this.update(this.activeDate.add("mo", -1));
16815     },
16816     showToday : function(e){
16817         this.update(new Date().clearTime());
16818     },
16819     // private
16820     showNextMonth : function(e){
16821         this.update(this.activeDate.add("mo", 1));
16822     },
16823
16824     // private
16825     showPrevYear : function(){
16826         this.update(this.activeDate.add("y", -1));
16827     },
16828
16829     // private
16830     showNextYear : function(){
16831         this.update(this.activeDate.add("y", 1));
16832     },
16833
16834     
16835    // private
16836     update : function(date)
16837     {
16838         var vd = this.activeDate;
16839         this.activeDate = date;
16840 //        if(vd && this.el){
16841 //            var t = date.getTime();
16842 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16843 //                Roo.log('using add remove');
16844 //                
16845 //                this.fireEvent('monthchange', this, date);
16846 //                
16847 //                this.cells.removeClass("fc-state-highlight");
16848 //                this.cells.each(function(c){
16849 //                   if(c.dateValue == t){
16850 //                       c.addClass("fc-state-highlight");
16851 //                       setTimeout(function(){
16852 //                            try{c.dom.firstChild.focus();}catch(e){}
16853 //                       }, 50);
16854 //                       return false;
16855 //                   }
16856 //                   return true;
16857 //                });
16858 //                return;
16859 //            }
16860 //        }
16861         
16862         var days = date.getDaysInMonth();
16863         
16864         var firstOfMonth = date.getFirstDateOfMonth();
16865         var startingPos = firstOfMonth.getDay()-this.startDay;
16866         
16867         if(startingPos < this.startDay){
16868             startingPos += 7;
16869         }
16870         
16871         var pm = date.add(Date.MONTH, -1);
16872         var prevStart = pm.getDaysInMonth()-startingPos;
16873 //        
16874         this.cells = this.el.select('.fc-day',true);
16875         this.textNodes = this.el.query('.fc-day-number');
16876         this.cells.addClassOnOver('fc-state-hover');
16877         
16878         var cells = this.cells.elements;
16879         var textEls = this.textNodes;
16880         
16881         Roo.each(cells, function(cell){
16882             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16883         });
16884         
16885         days += startingPos;
16886
16887         // convert everything to numbers so it's fast
16888         var day = 86400000;
16889         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16890         //Roo.log(d);
16891         //Roo.log(pm);
16892         //Roo.log(prevStart);
16893         
16894         var today = new Date().clearTime().getTime();
16895         var sel = date.clearTime().getTime();
16896         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16897         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16898         var ddMatch = this.disabledDatesRE;
16899         var ddText = this.disabledDatesText;
16900         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16901         var ddaysText = this.disabledDaysText;
16902         var format = this.format;
16903         
16904         var setCellClass = function(cal, cell){
16905             cell.row = 0;
16906             cell.events = [];
16907             cell.more = [];
16908             //Roo.log('set Cell Class');
16909             cell.title = "";
16910             var t = d.getTime();
16911             
16912             //Roo.log(d);
16913             
16914             cell.dateValue = t;
16915             if(t == today){
16916                 cell.className += " fc-today";
16917                 cell.className += " fc-state-highlight";
16918                 cell.title = cal.todayText;
16919             }
16920             if(t == sel){
16921                 // disable highlight in other month..
16922                 //cell.className += " fc-state-highlight";
16923                 
16924             }
16925             // disabling
16926             if(t < min) {
16927                 cell.className = " fc-state-disabled";
16928                 cell.title = cal.minText;
16929                 return;
16930             }
16931             if(t > max) {
16932                 cell.className = " fc-state-disabled";
16933                 cell.title = cal.maxText;
16934                 return;
16935             }
16936             if(ddays){
16937                 if(ddays.indexOf(d.getDay()) != -1){
16938                     cell.title = ddaysText;
16939                     cell.className = " fc-state-disabled";
16940                 }
16941             }
16942             if(ddMatch && format){
16943                 var fvalue = d.dateFormat(format);
16944                 if(ddMatch.test(fvalue)){
16945                     cell.title = ddText.replace("%0", fvalue);
16946                     cell.className = " fc-state-disabled";
16947                 }
16948             }
16949             
16950             if (!cell.initialClassName) {
16951                 cell.initialClassName = cell.dom.className;
16952             }
16953             
16954             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16955         };
16956
16957         var i = 0;
16958         
16959         for(; i < startingPos; i++) {
16960             textEls[i].innerHTML = (++prevStart);
16961             d.setDate(d.getDate()+1);
16962             
16963             cells[i].className = "fc-past fc-other-month";
16964             setCellClass(this, cells[i]);
16965         }
16966         
16967         var intDay = 0;
16968         
16969         for(; i < days; i++){
16970             intDay = i - startingPos + 1;
16971             textEls[i].innerHTML = (intDay);
16972             d.setDate(d.getDate()+1);
16973             
16974             cells[i].className = ''; // "x-date-active";
16975             setCellClass(this, cells[i]);
16976         }
16977         var extraDays = 0;
16978         
16979         for(; i < 42; i++) {
16980             textEls[i].innerHTML = (++extraDays);
16981             d.setDate(d.getDate()+1);
16982             
16983             cells[i].className = "fc-future fc-other-month";
16984             setCellClass(this, cells[i]);
16985         }
16986         
16987         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16988         
16989         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16990         
16991         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16992         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16993         
16994         if(totalRows != 6){
16995             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16996             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16997         }
16998         
16999         this.fireEvent('monthchange', this, date);
17000         
17001         
17002         /*
17003         if(!this.internalRender){
17004             var main = this.el.dom.firstChild;
17005             var w = main.offsetWidth;
17006             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17007             Roo.fly(main).setWidth(w);
17008             this.internalRender = true;
17009             // opera does not respect the auto grow header center column
17010             // then, after it gets a width opera refuses to recalculate
17011             // without a second pass
17012             if(Roo.isOpera && !this.secondPass){
17013                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17014                 this.secondPass = true;
17015                 this.update.defer(10, this, [date]);
17016             }
17017         }
17018         */
17019         
17020     },
17021     
17022     findCell : function(dt) {
17023         dt = dt.clearTime().getTime();
17024         var ret = false;
17025         this.cells.each(function(c){
17026             //Roo.log("check " +c.dateValue + '?=' + dt);
17027             if(c.dateValue == dt){
17028                 ret = c;
17029                 return false;
17030             }
17031             return true;
17032         });
17033         
17034         return ret;
17035     },
17036     
17037     findCells : function(ev) {
17038         var s = ev.start.clone().clearTime().getTime();
17039        // Roo.log(s);
17040         var e= ev.end.clone().clearTime().getTime();
17041        // Roo.log(e);
17042         var ret = [];
17043         this.cells.each(function(c){
17044              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17045             
17046             if(c.dateValue > e){
17047                 return ;
17048             }
17049             if(c.dateValue < s){
17050                 return ;
17051             }
17052             ret.push(c);
17053         });
17054         
17055         return ret;    
17056     },
17057     
17058 //    findBestRow: function(cells)
17059 //    {
17060 //        var ret = 0;
17061 //        
17062 //        for (var i =0 ; i < cells.length;i++) {
17063 //            ret  = Math.max(cells[i].rows || 0,ret);
17064 //        }
17065 //        return ret;
17066 //        
17067 //    },
17068     
17069     
17070     addItem : function(ev)
17071     {
17072         // look for vertical location slot in
17073         var cells = this.findCells(ev);
17074         
17075 //        ev.row = this.findBestRow(cells);
17076         
17077         // work out the location.
17078         
17079         var crow = false;
17080         var rows = [];
17081         for(var i =0; i < cells.length; i++) {
17082             
17083             cells[i].row = cells[0].row;
17084             
17085             if(i == 0){
17086                 cells[i].row = cells[i].row + 1;
17087             }
17088             
17089             if (!crow) {
17090                 crow = {
17091                     start : cells[i],
17092                     end :  cells[i]
17093                 };
17094                 continue;
17095             }
17096             if (crow.start.getY() == cells[i].getY()) {
17097                 // on same row.
17098                 crow.end = cells[i];
17099                 continue;
17100             }
17101             // different row.
17102             rows.push(crow);
17103             crow = {
17104                 start: cells[i],
17105                 end : cells[i]
17106             };
17107             
17108         }
17109         
17110         rows.push(crow);
17111         ev.els = [];
17112         ev.rows = rows;
17113         ev.cells = cells;
17114         
17115         cells[0].events.push(ev);
17116         
17117         this.calevents.push(ev);
17118     },
17119     
17120     clearEvents: function() {
17121         
17122         if(!this.calevents){
17123             return;
17124         }
17125         
17126         Roo.each(this.cells.elements, function(c){
17127             c.row = 0;
17128             c.events = [];
17129             c.more = [];
17130         });
17131         
17132         Roo.each(this.calevents, function(e) {
17133             Roo.each(e.els, function(el) {
17134                 el.un('mouseenter' ,this.onEventEnter, this);
17135                 el.un('mouseleave' ,this.onEventLeave, this);
17136                 el.remove();
17137             },this);
17138         },this);
17139         
17140         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17141             e.remove();
17142         });
17143         
17144     },
17145     
17146     renderEvents: function()
17147     {   
17148         var _this = this;
17149         
17150         this.cells.each(function(c) {
17151             
17152             if(c.row < 5){
17153                 return;
17154             }
17155             
17156             var ev = c.events;
17157             
17158             var r = 4;
17159             if(c.row != c.events.length){
17160                 r = 4 - (4 - (c.row - c.events.length));
17161             }
17162             
17163             c.events = ev.slice(0, r);
17164             c.more = ev.slice(r);
17165             
17166             if(c.more.length && c.more.length == 1){
17167                 c.events.push(c.more.pop());
17168             }
17169             
17170             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17171             
17172         });
17173             
17174         this.cells.each(function(c) {
17175             
17176             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17177             
17178             
17179             for (var e = 0; e < c.events.length; e++){
17180                 var ev = c.events[e];
17181                 var rows = ev.rows;
17182                 
17183                 for(var i = 0; i < rows.length; i++) {
17184                 
17185                     // how many rows should it span..
17186
17187                     var  cfg = {
17188                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17189                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17190
17191                         unselectable : "on",
17192                         cn : [
17193                             {
17194                                 cls: 'fc-event-inner',
17195                                 cn : [
17196     //                                {
17197     //                                  tag:'span',
17198     //                                  cls: 'fc-event-time',
17199     //                                  html : cells.length > 1 ? '' : ev.time
17200     //                                },
17201                                     {
17202                                       tag:'span',
17203                                       cls: 'fc-event-title',
17204                                       html : String.format('{0}', ev.title)
17205                                     }
17206
17207
17208                                 ]
17209                             },
17210                             {
17211                                 cls: 'ui-resizable-handle ui-resizable-e',
17212                                 html : '&nbsp;&nbsp;&nbsp'
17213                             }
17214
17215                         ]
17216                     };
17217
17218                     if (i == 0) {
17219                         cfg.cls += ' fc-event-start';
17220                     }
17221                     if ((i+1) == rows.length) {
17222                         cfg.cls += ' fc-event-end';
17223                     }
17224
17225                     var ctr = _this.el.select('.fc-event-container',true).first();
17226                     var cg = ctr.createChild(cfg);
17227
17228                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17229                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17230
17231                     var r = (c.more.length) ? 1 : 0;
17232                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17233                     cg.setWidth(ebox.right - sbox.x -2);
17234
17235                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17236                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17237                     cg.on('click', _this.onEventClick, _this, ev);
17238
17239                     ev.els.push(cg);
17240                     
17241                 }
17242                 
17243             }
17244             
17245             
17246             if(c.more.length){
17247                 var  cfg = {
17248                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17249                     style : 'position: absolute',
17250                     unselectable : "on",
17251                     cn : [
17252                         {
17253                             cls: 'fc-event-inner',
17254                             cn : [
17255                                 {
17256                                   tag:'span',
17257                                   cls: 'fc-event-title',
17258                                   html : 'More'
17259                                 }
17260
17261
17262                             ]
17263                         },
17264                         {
17265                             cls: 'ui-resizable-handle ui-resizable-e',
17266                             html : '&nbsp;&nbsp;&nbsp'
17267                         }
17268
17269                     ]
17270                 };
17271
17272                 var ctr = _this.el.select('.fc-event-container',true).first();
17273                 var cg = ctr.createChild(cfg);
17274
17275                 var sbox = c.select('.fc-day-content',true).first().getBox();
17276                 var ebox = c.select('.fc-day-content',true).first().getBox();
17277                 //Roo.log(cg);
17278                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17279                 cg.setWidth(ebox.right - sbox.x -2);
17280
17281                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17282                 
17283             }
17284             
17285         });
17286         
17287         
17288         
17289     },
17290     
17291     onEventEnter: function (e, el,event,d) {
17292         this.fireEvent('evententer', this, el, event);
17293     },
17294     
17295     onEventLeave: function (e, el,event,d) {
17296         this.fireEvent('eventleave', this, el, event);
17297     },
17298     
17299     onEventClick: function (e, el,event,d) {
17300         this.fireEvent('eventclick', this, el, event);
17301     },
17302     
17303     onMonthChange: function () {
17304         this.store.load();
17305     },
17306     
17307     onMoreEventClick: function(e, el, more)
17308     {
17309         var _this = this;
17310         
17311         this.calpopover.placement = 'right';
17312         this.calpopover.setTitle('More');
17313         
17314         this.calpopover.setContent('');
17315         
17316         var ctr = this.calpopover.el.select('.popover-content', true).first();
17317         
17318         Roo.each(more, function(m){
17319             var cfg = {
17320                 cls : 'fc-event-hori fc-event-draggable',
17321                 html : m.title
17322             };
17323             var cg = ctr.createChild(cfg);
17324             
17325             cg.on('click', _this.onEventClick, _this, m);
17326         });
17327         
17328         this.calpopover.show(el);
17329         
17330         
17331     },
17332     
17333     onLoad: function () 
17334     {   
17335         this.calevents = [];
17336         var cal = this;
17337         
17338         if(this.store.getCount() > 0){
17339             this.store.data.each(function(d){
17340                cal.addItem({
17341                     id : d.data.id,
17342                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17343                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17344                     time : d.data.start_time,
17345                     title : d.data.title,
17346                     description : d.data.description,
17347                     venue : d.data.venue
17348                 });
17349             });
17350         }
17351         
17352         this.renderEvents();
17353         
17354         if(this.calevents.length && this.loadMask){
17355             this.maskEl.hide();
17356         }
17357     },
17358     
17359     onBeforeLoad: function()
17360     {
17361         this.clearEvents();
17362         if(this.loadMask){
17363             this.maskEl.show();
17364         }
17365     }
17366 });
17367
17368  
17369  /*
17370  * - LGPL
17371  *
17372  * element
17373  * 
17374  */
17375
17376 /**
17377  * @class Roo.bootstrap.Popover
17378  * @extends Roo.bootstrap.Component
17379  * Bootstrap Popover class
17380  * @cfg {String} html contents of the popover   (or false to use children..)
17381  * @cfg {String} title of popover (or false to hide)
17382  * @cfg {String} placement how it is placed
17383  * @cfg {String} trigger click || hover (or false to trigger manually)
17384  * @cfg {String} over what (parent or false to trigger manually.)
17385  * @cfg {Number} delay - delay before showing
17386  
17387  * @constructor
17388  * Create a new Popover
17389  * @param {Object} config The config object
17390  */
17391
17392 Roo.bootstrap.Popover = function(config){
17393     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17394     
17395     this.addEvents({
17396         // raw events
17397          /**
17398          * @event show
17399          * After the popover show
17400          * 
17401          * @param {Roo.bootstrap.Popover} this
17402          */
17403         "show" : true,
17404         /**
17405          * @event hide
17406          * After the popover hide
17407          * 
17408          * @param {Roo.bootstrap.Popover} this
17409          */
17410         "hide" : true
17411     });
17412 };
17413
17414 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17415     
17416     title: 'Fill in a title',
17417     html: false,
17418     
17419     placement : 'right',
17420     trigger : 'hover', // hover
17421     
17422     delay : 0,
17423     
17424     over: 'parent',
17425     
17426     can_build_overlaid : false,
17427     
17428     getChildContainer : function()
17429     {
17430         return this.el.select('.popover-content',true).first();
17431     },
17432     
17433     getAutoCreate : function(){
17434          
17435         var cfg = {
17436            cls : 'popover roo-dynamic',
17437            style: 'display:block',
17438            cn : [
17439                 {
17440                     cls : 'arrow'
17441                 },
17442                 {
17443                     cls : 'popover-inner',
17444                     cn : [
17445                         {
17446                             tag: 'h3',
17447                             cls: 'popover-title',
17448                             html : this.title
17449                         },
17450                         {
17451                             cls : 'popover-content',
17452                             html : this.html
17453                         }
17454                     ]
17455                     
17456                 }
17457            ]
17458         };
17459         
17460         return cfg;
17461     },
17462     setTitle: function(str)
17463     {
17464         this.title = str;
17465         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17466     },
17467     setContent: function(str)
17468     {
17469         this.html = str;
17470         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17471     },
17472     // as it get's added to the bottom of the page.
17473     onRender : function(ct, position)
17474     {
17475         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17476         if(!this.el){
17477             var cfg = Roo.apply({},  this.getAutoCreate());
17478             cfg.id = Roo.id();
17479             
17480             if (this.cls) {
17481                 cfg.cls += ' ' + this.cls;
17482             }
17483             if (this.style) {
17484                 cfg.style = this.style;
17485             }
17486             //Roo.log("adding to ");
17487             this.el = Roo.get(document.body).createChild(cfg, position);
17488 //            Roo.log(this.el);
17489         }
17490         this.initEvents();
17491     },
17492     
17493     initEvents : function()
17494     {
17495         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17496         this.el.enableDisplayMode('block');
17497         this.el.hide();
17498         if (this.over === false) {
17499             return; 
17500         }
17501         if (this.triggers === false) {
17502             return;
17503         }
17504         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17505         var triggers = this.trigger ? this.trigger.split(' ') : [];
17506         Roo.each(triggers, function(trigger) {
17507         
17508             if (trigger == 'click') {
17509                 on_el.on('click', this.toggle, this);
17510             } else if (trigger != 'manual') {
17511                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17512                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17513       
17514                 on_el.on(eventIn  ,this.enter, this);
17515                 on_el.on(eventOut, this.leave, this);
17516             }
17517         }, this);
17518         
17519     },
17520     
17521     
17522     // private
17523     timeout : null,
17524     hoverState : null,
17525     
17526     toggle : function () {
17527         this.hoverState == 'in' ? this.leave() : this.enter();
17528     },
17529     
17530     enter : function () {
17531         
17532         clearTimeout(this.timeout);
17533     
17534         this.hoverState = 'in';
17535     
17536         if (!this.delay || !this.delay.show) {
17537             this.show();
17538             return;
17539         }
17540         var _t = this;
17541         this.timeout = setTimeout(function () {
17542             if (_t.hoverState == 'in') {
17543                 _t.show();
17544             }
17545         }, this.delay.show)
17546     },
17547     
17548     leave : function() {
17549         clearTimeout(this.timeout);
17550     
17551         this.hoverState = 'out';
17552     
17553         if (!this.delay || !this.delay.hide) {
17554             this.hide();
17555             return;
17556         }
17557         var _t = this;
17558         this.timeout = setTimeout(function () {
17559             if (_t.hoverState == 'out') {
17560                 _t.hide();
17561             }
17562         }, this.delay.hide)
17563     },
17564     
17565     show : function (on_el)
17566     {
17567         if (!on_el) {
17568             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17569         }
17570         
17571         // set content.
17572         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17573         if (this.html !== false) {
17574             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17575         }
17576         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17577         if (!this.title.length) {
17578             this.el.select('.popover-title',true).hide();
17579         }
17580         
17581         var placement = typeof this.placement == 'function' ?
17582             this.placement.call(this, this.el, on_el) :
17583             this.placement;
17584             
17585         var autoToken = /\s?auto?\s?/i;
17586         var autoPlace = autoToken.test(placement);
17587         if (autoPlace) {
17588             placement = placement.replace(autoToken, '') || 'top';
17589         }
17590         
17591         //this.el.detach()
17592         //this.el.setXY([0,0]);
17593         this.el.show();
17594         this.el.dom.style.display='block';
17595         this.el.addClass(placement);
17596         
17597         //this.el.appendTo(on_el);
17598         
17599         var p = this.getPosition();
17600         var box = this.el.getBox();
17601         
17602         if (autoPlace) {
17603             // fixme..
17604         }
17605         var align = Roo.bootstrap.Popover.alignment[placement];
17606         
17607 //        Roo.log(align);
17608         this.el.alignTo(on_el, align[0],align[1]);
17609         //var arrow = this.el.select('.arrow',true).first();
17610         //arrow.set(align[2], 
17611         
17612         this.el.addClass('in');
17613         
17614         
17615         if (this.el.hasClass('fade')) {
17616             // fade it?
17617         }
17618         
17619         this.hoverState = 'in';
17620         
17621         this.fireEvent('show', this);
17622         
17623     },
17624     hide : function()
17625     {
17626         this.el.setXY([0,0]);
17627         this.el.removeClass('in');
17628         this.el.hide();
17629         this.hoverState = null;
17630         
17631         this.fireEvent('hide', this);
17632     }
17633     
17634 });
17635
17636 Roo.bootstrap.Popover.alignment = {
17637     'left' : ['r-l', [-10,0], 'right'],
17638     'right' : ['l-r', [10,0], 'left'],
17639     'bottom' : ['t-b', [0,10], 'top'],
17640     'top' : [ 'b-t', [0,-10], 'bottom']
17641 };
17642
17643  /*
17644  * - LGPL
17645  *
17646  * Progress
17647  * 
17648  */
17649
17650 /**
17651  * @class Roo.bootstrap.Progress
17652  * @extends Roo.bootstrap.Component
17653  * Bootstrap Progress class
17654  * @cfg {Boolean} striped striped of the progress bar
17655  * @cfg {Boolean} active animated of the progress bar
17656  * 
17657  * 
17658  * @constructor
17659  * Create a new Progress
17660  * @param {Object} config The config object
17661  */
17662
17663 Roo.bootstrap.Progress = function(config){
17664     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17665 };
17666
17667 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17668     
17669     striped : false,
17670     active: false,
17671     
17672     getAutoCreate : function(){
17673         var cfg = {
17674             tag: 'div',
17675             cls: 'progress'
17676         };
17677         
17678         
17679         if(this.striped){
17680             cfg.cls += ' progress-striped';
17681         }
17682       
17683         if(this.active){
17684             cfg.cls += ' active';
17685         }
17686         
17687         
17688         return cfg;
17689     }
17690    
17691 });
17692
17693  
17694
17695  /*
17696  * - LGPL
17697  *
17698  * ProgressBar
17699  * 
17700  */
17701
17702 /**
17703  * @class Roo.bootstrap.ProgressBar
17704  * @extends Roo.bootstrap.Component
17705  * Bootstrap ProgressBar class
17706  * @cfg {Number} aria_valuenow aria-value now
17707  * @cfg {Number} aria_valuemin aria-value min
17708  * @cfg {Number} aria_valuemax aria-value max
17709  * @cfg {String} label label for the progress bar
17710  * @cfg {String} panel (success | info | warning | danger )
17711  * @cfg {String} role role of the progress bar
17712  * @cfg {String} sr_only text
17713  * 
17714  * 
17715  * @constructor
17716  * Create a new ProgressBar
17717  * @param {Object} config The config object
17718  */
17719
17720 Roo.bootstrap.ProgressBar = function(config){
17721     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17722 };
17723
17724 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17725     
17726     aria_valuenow : 0,
17727     aria_valuemin : 0,
17728     aria_valuemax : 100,
17729     label : false,
17730     panel : false,
17731     role : false,
17732     sr_only: false,
17733     
17734     getAutoCreate : function()
17735     {
17736         
17737         var cfg = {
17738             tag: 'div',
17739             cls: 'progress-bar',
17740             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17741         };
17742         
17743         if(this.sr_only){
17744             cfg.cn = {
17745                 tag: 'span',
17746                 cls: 'sr-only',
17747                 html: this.sr_only
17748             }
17749         }
17750         
17751         if(this.role){
17752             cfg.role = this.role;
17753         }
17754         
17755         if(this.aria_valuenow){
17756             cfg['aria-valuenow'] = this.aria_valuenow;
17757         }
17758         
17759         if(this.aria_valuemin){
17760             cfg['aria-valuemin'] = this.aria_valuemin;
17761         }
17762         
17763         if(this.aria_valuemax){
17764             cfg['aria-valuemax'] = this.aria_valuemax;
17765         }
17766         
17767         if(this.label && !this.sr_only){
17768             cfg.html = this.label;
17769         }
17770         
17771         if(this.panel){
17772             cfg.cls += ' progress-bar-' + this.panel;
17773         }
17774         
17775         return cfg;
17776     },
17777     
17778     update : function(aria_valuenow)
17779     {
17780         this.aria_valuenow = aria_valuenow;
17781         
17782         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17783     }
17784    
17785 });
17786
17787  
17788
17789  /*
17790  * - LGPL
17791  *
17792  * column
17793  * 
17794  */
17795
17796 /**
17797  * @class Roo.bootstrap.TabGroup
17798  * @extends Roo.bootstrap.Column
17799  * Bootstrap Column class
17800  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17801  * @cfg {Boolean} carousel true to make the group behave like a carousel
17802  * @cfg {Boolean} bullets show bullets for the panels
17803  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17804  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17805  * @cfg {Boolean} showarrow (true|false) show arrow default true
17806  * 
17807  * @constructor
17808  * Create a new TabGroup
17809  * @param {Object} config The config object
17810  */
17811
17812 Roo.bootstrap.TabGroup = function(config){
17813     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17814     if (!this.navId) {
17815         this.navId = Roo.id();
17816     }
17817     this.tabs = [];
17818     Roo.bootstrap.TabGroup.register(this);
17819     
17820 };
17821
17822 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17823     
17824     carousel : false,
17825     transition : false,
17826     bullets : 0,
17827     timer : 0,
17828     autoslide : false,
17829     slideFn : false,
17830     slideOnTouch : false,
17831     showarrow : true,
17832     
17833     getAutoCreate : function()
17834     {
17835         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17836         
17837         cfg.cls += ' tab-content';
17838         
17839         if (this.carousel) {
17840             cfg.cls += ' carousel slide';
17841             
17842             cfg.cn = [{
17843                cls : 'carousel-inner',
17844                cn : []
17845             }];
17846         
17847             if(this.bullets  && !Roo.isTouch){
17848                 
17849                 var bullets = {
17850                     cls : 'carousel-bullets',
17851                     cn : []
17852                 };
17853                
17854                 if(this.bullets_cls){
17855                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17856                 }
17857                 
17858                 bullets.cn.push({
17859                     cls : 'clear'
17860                 });
17861                 
17862                 cfg.cn[0].cn.push(bullets);
17863             }
17864             
17865             if(this.showarrow){
17866                 cfg.cn[0].cn.push({
17867                     tag : 'div',
17868                     class : 'carousel-arrow',
17869                     cn : [
17870                         {
17871                             tag : 'div',
17872                             class : 'carousel-prev',
17873                             cn : [
17874                                 {
17875                                     tag : 'i',
17876                                     class : 'fa fa-chevron-left'
17877                                 }
17878                             ]
17879                         },
17880                         {
17881                             tag : 'div',
17882                             class : 'carousel-next',
17883                             cn : [
17884                                 {
17885                                     tag : 'i',
17886                                     class : 'fa fa-chevron-right'
17887                                 }
17888                             ]
17889                         }
17890                     ]
17891                 });
17892             }
17893             
17894         }
17895         
17896         return cfg;
17897     },
17898     
17899     initEvents:  function()
17900     {
17901 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17902 //            this.el.on("touchstart", this.onTouchStart, this);
17903 //        }
17904         
17905         if(this.autoslide){
17906             var _this = this;
17907             
17908             this.slideFn = window.setInterval(function() {
17909                 _this.showPanelNext();
17910             }, this.timer);
17911         }
17912         
17913         if(this.showarrow){
17914             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17915             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17916         }
17917         
17918         
17919     },
17920     
17921 //    onTouchStart : function(e, el, o)
17922 //    {
17923 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17924 //            return;
17925 //        }
17926 //        
17927 //        this.showPanelNext();
17928 //    },
17929     
17930     
17931     getChildContainer : function()
17932     {
17933         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17934     },
17935     
17936     /**
17937     * register a Navigation item
17938     * @param {Roo.bootstrap.NavItem} the navitem to add
17939     */
17940     register : function(item)
17941     {
17942         this.tabs.push( item);
17943         item.navId = this.navId; // not really needed..
17944         this.addBullet();
17945     
17946     },
17947     
17948     getActivePanel : function()
17949     {
17950         var r = false;
17951         Roo.each(this.tabs, function(t) {
17952             if (t.active) {
17953                 r = t;
17954                 return false;
17955             }
17956             return null;
17957         });
17958         return r;
17959         
17960     },
17961     getPanelByName : function(n)
17962     {
17963         var r = false;
17964         Roo.each(this.tabs, function(t) {
17965             if (t.tabId == n) {
17966                 r = t;
17967                 return false;
17968             }
17969             return null;
17970         });
17971         return r;
17972     },
17973     indexOfPanel : function(p)
17974     {
17975         var r = false;
17976         Roo.each(this.tabs, function(t,i) {
17977             if (t.tabId == p.tabId) {
17978                 r = i;
17979                 return false;
17980             }
17981             return null;
17982         });
17983         return r;
17984     },
17985     /**
17986      * show a specific panel
17987      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17988      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17989      */
17990     showPanel : function (pan)
17991     {
17992         if(this.transition || typeof(pan) == 'undefined'){
17993             Roo.log("waiting for the transitionend");
17994             return;
17995         }
17996         
17997         if (typeof(pan) == 'number') {
17998             pan = this.tabs[pan];
17999         }
18000         
18001         if (typeof(pan) == 'string') {
18002             pan = this.getPanelByName(pan);
18003         }
18004         
18005         var cur = this.getActivePanel();
18006         
18007         if(!pan || !cur){
18008             Roo.log('pan or acitve pan is undefined');
18009             return false;
18010         }
18011         
18012         if (pan.tabId == this.getActivePanel().tabId) {
18013             return true;
18014         }
18015         
18016         if (false === cur.fireEvent('beforedeactivate')) {
18017             return false;
18018         }
18019         
18020         if(this.bullets > 0 && !Roo.isTouch){
18021             this.setActiveBullet(this.indexOfPanel(pan));
18022         }
18023         
18024         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18025             
18026             this.transition = true;
18027             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18028             var lr = dir == 'next' ? 'left' : 'right';
18029             pan.el.addClass(dir); // or prev
18030             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18031             cur.el.addClass(lr); // or right
18032             pan.el.addClass(lr);
18033             
18034             var _this = this;
18035             cur.el.on('transitionend', function() {
18036                 Roo.log("trans end?");
18037                 
18038                 pan.el.removeClass([lr,dir]);
18039                 pan.setActive(true);
18040                 
18041                 cur.el.removeClass([lr]);
18042                 cur.setActive(false);
18043                 
18044                 _this.transition = false;
18045                 
18046             }, this, { single:  true } );
18047             
18048             return true;
18049         }
18050         
18051         cur.setActive(false);
18052         pan.setActive(true);
18053         
18054         return true;
18055         
18056     },
18057     showPanelNext : function()
18058     {
18059         var i = this.indexOfPanel(this.getActivePanel());
18060         
18061         if (i >= this.tabs.length - 1 && !this.autoslide) {
18062             return;
18063         }
18064         
18065         if (i >= this.tabs.length - 1 && this.autoslide) {
18066             i = -1;
18067         }
18068         
18069         this.showPanel(this.tabs[i+1]);
18070     },
18071     
18072     showPanelPrev : function()
18073     {
18074         var i = this.indexOfPanel(this.getActivePanel());
18075         
18076         if (i  < 1 && !this.autoslide) {
18077             return;
18078         }
18079         
18080         if (i < 1 && this.autoslide) {
18081             i = this.tabs.length;
18082         }
18083         
18084         this.showPanel(this.tabs[i-1]);
18085     },
18086     
18087     
18088     addBullet: function()
18089     {
18090         if(!this.bullets || Roo.isTouch){
18091             return;
18092         }
18093         var ctr = this.el.select('.carousel-bullets',true).first();
18094         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18095         var bullet = ctr.createChild({
18096             cls : 'bullet bullet-' + i
18097         },ctr.dom.lastChild);
18098         
18099         
18100         var _this = this;
18101         
18102         bullet.on('click', (function(e, el, o, ii, t){
18103
18104             e.preventDefault();
18105
18106             this.showPanel(ii);
18107
18108             if(this.autoslide && this.slideFn){
18109                 clearInterval(this.slideFn);
18110                 this.slideFn = window.setInterval(function() {
18111                     _this.showPanelNext();
18112                 }, this.timer);
18113             }
18114
18115         }).createDelegate(this, [i, bullet], true));
18116                 
18117         
18118     },
18119      
18120     setActiveBullet : function(i)
18121     {
18122         if(Roo.isTouch){
18123             return;
18124         }
18125         
18126         Roo.each(this.el.select('.bullet', true).elements, function(el){
18127             el.removeClass('selected');
18128         });
18129
18130         var bullet = this.el.select('.bullet-' + i, true).first();
18131         
18132         if(!bullet){
18133             return;
18134         }
18135         
18136         bullet.addClass('selected');
18137     }
18138     
18139     
18140   
18141 });
18142
18143  
18144
18145  
18146  
18147 Roo.apply(Roo.bootstrap.TabGroup, {
18148     
18149     groups: {},
18150      /**
18151     * register a Navigation Group
18152     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18153     */
18154     register : function(navgrp)
18155     {
18156         this.groups[navgrp.navId] = navgrp;
18157         
18158     },
18159     /**
18160     * fetch a Navigation Group based on the navigation ID
18161     * if one does not exist , it will get created.
18162     * @param {string} the navgroup to add
18163     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18164     */
18165     get: function(navId) {
18166         if (typeof(this.groups[navId]) == 'undefined') {
18167             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18168         }
18169         return this.groups[navId] ;
18170     }
18171     
18172     
18173     
18174 });
18175
18176  /*
18177  * - LGPL
18178  *
18179  * TabPanel
18180  * 
18181  */
18182
18183 /**
18184  * @class Roo.bootstrap.TabPanel
18185  * @extends Roo.bootstrap.Component
18186  * Bootstrap TabPanel class
18187  * @cfg {Boolean} active panel active
18188  * @cfg {String} html panel content
18189  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18190  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18191  * @cfg {String} href click to link..
18192  * 
18193  * 
18194  * @constructor
18195  * Create a new TabPanel
18196  * @param {Object} config The config object
18197  */
18198
18199 Roo.bootstrap.TabPanel = function(config){
18200     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18201     this.addEvents({
18202         /**
18203              * @event changed
18204              * Fires when the active status changes
18205              * @param {Roo.bootstrap.TabPanel} this
18206              * @param {Boolean} state the new state
18207             
18208          */
18209         'changed': true,
18210         /**
18211              * @event beforedeactivate
18212              * Fires before a tab is de-activated - can be used to do validation on a form.
18213              * @param {Roo.bootstrap.TabPanel} this
18214              * @return {Boolean} false if there is an error
18215             
18216          */
18217         'beforedeactivate': true
18218      });
18219     
18220     this.tabId = this.tabId || Roo.id();
18221   
18222 };
18223
18224 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18225     
18226     active: false,
18227     html: false,
18228     tabId: false,
18229     navId : false,
18230     href : '',
18231     
18232     getAutoCreate : function(){
18233         var cfg = {
18234             tag: 'div',
18235             // item is needed for carousel - not sure if it has any effect otherwise
18236             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18237             html: this.html || ''
18238         };
18239         
18240         if(this.active){
18241             cfg.cls += ' active';
18242         }
18243         
18244         if(this.tabId){
18245             cfg.tabId = this.tabId;
18246         }
18247         
18248         
18249         return cfg;
18250     },
18251     
18252     initEvents:  function()
18253     {
18254         var p = this.parent();
18255         
18256         this.navId = this.navId || p.navId;
18257         
18258         if (typeof(this.navId) != 'undefined') {
18259             // not really needed.. but just in case.. parent should be a NavGroup.
18260             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18261             
18262             tg.register(this);
18263             
18264             var i = tg.tabs.length - 1;
18265             
18266             if(this.active && tg.bullets > 0 && i < tg.bullets){
18267                 tg.setActiveBullet(i);
18268             }
18269         }
18270         
18271         this.el.on('click', this.onClick, this);
18272         
18273         if(Roo.isTouch){
18274             this.el.on("touchstart", this.onTouchStart, this);
18275             this.el.on("touchmove", this.onTouchMove, this);
18276             this.el.on("touchend", this.onTouchEnd, this);
18277         }
18278         
18279     },
18280     
18281     onRender : function(ct, position)
18282     {
18283         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18284     },
18285     
18286     setActive : function(state)
18287     {
18288         Roo.log("panel - set active " + this.tabId + "=" + state);
18289         
18290         this.active = state;
18291         if (!state) {
18292             this.el.removeClass('active');
18293             
18294         } else  if (!this.el.hasClass('active')) {
18295             this.el.addClass('active');
18296         }
18297         
18298         this.fireEvent('changed', this, state);
18299     },
18300     
18301     onClick : function(e)
18302     {
18303         e.preventDefault();
18304         
18305         if(!this.href.length){
18306             return;
18307         }
18308         
18309         window.location.href = this.href;
18310     },
18311     
18312     startX : 0,
18313     startY : 0,
18314     endX : 0,
18315     endY : 0,
18316     swiping : false,
18317     
18318     onTouchStart : function(e)
18319     {
18320         this.swiping = false;
18321         
18322         this.startX = e.browserEvent.touches[0].clientX;
18323         this.startY = e.browserEvent.touches[0].clientY;
18324     },
18325     
18326     onTouchMove : function(e)
18327     {
18328         this.swiping = true;
18329         
18330         this.endX = e.browserEvent.touches[0].clientX;
18331         this.endY = e.browserEvent.touches[0].clientY;
18332     },
18333     
18334     onTouchEnd : function(e)
18335     {
18336         if(!this.swiping){
18337             this.onClick(e);
18338             return;
18339         }
18340         
18341         var tabGroup = this.parent();
18342         
18343         if(this.endX > this.startX){ // swiping right
18344             tabGroup.showPanelPrev();
18345             return;
18346         }
18347         
18348         if(this.startX > this.endX){ // swiping left
18349             tabGroup.showPanelNext();
18350             return;
18351         }
18352     }
18353     
18354     
18355 });
18356  
18357
18358  
18359
18360  /*
18361  * - LGPL
18362  *
18363  * DateField
18364  * 
18365  */
18366
18367 /**
18368  * @class Roo.bootstrap.DateField
18369  * @extends Roo.bootstrap.Input
18370  * Bootstrap DateField class
18371  * @cfg {Number} weekStart default 0
18372  * @cfg {String} viewMode default empty, (months|years)
18373  * @cfg {String} minViewMode default empty, (months|years)
18374  * @cfg {Number} startDate default -Infinity
18375  * @cfg {Number} endDate default Infinity
18376  * @cfg {Boolean} todayHighlight default false
18377  * @cfg {Boolean} todayBtn default false
18378  * @cfg {Boolean} calendarWeeks default false
18379  * @cfg {Object} daysOfWeekDisabled default empty
18380  * @cfg {Boolean} singleMode default false (true | false)
18381  * 
18382  * @cfg {Boolean} keyboardNavigation default true
18383  * @cfg {String} language default en
18384  * 
18385  * @constructor
18386  * Create a new DateField
18387  * @param {Object} config The config object
18388  */
18389
18390 Roo.bootstrap.DateField = function(config){
18391     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18392      this.addEvents({
18393             /**
18394              * @event show
18395              * Fires when this field show.
18396              * @param {Roo.bootstrap.DateField} this
18397              * @param {Mixed} date The date value
18398              */
18399             show : true,
18400             /**
18401              * @event show
18402              * Fires when this field hide.
18403              * @param {Roo.bootstrap.DateField} this
18404              * @param {Mixed} date The date value
18405              */
18406             hide : true,
18407             /**
18408              * @event select
18409              * Fires when select a date.
18410              * @param {Roo.bootstrap.DateField} this
18411              * @param {Mixed} date The date value
18412              */
18413             select : true,
18414             /**
18415              * @event beforeselect
18416              * Fires when before select a date.
18417              * @param {Roo.bootstrap.DateField} this
18418              * @param {Mixed} date The date value
18419              */
18420             beforeselect : true
18421         });
18422 };
18423
18424 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18425     
18426     /**
18427      * @cfg {String} format
18428      * The default date format string which can be overriden for localization support.  The format must be
18429      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18430      */
18431     format : "m/d/y",
18432     /**
18433      * @cfg {String} altFormats
18434      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18435      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18436      */
18437     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18438     
18439     weekStart : 0,
18440     
18441     viewMode : '',
18442     
18443     minViewMode : '',
18444     
18445     todayHighlight : false,
18446     
18447     todayBtn: false,
18448     
18449     language: 'en',
18450     
18451     keyboardNavigation: true,
18452     
18453     calendarWeeks: false,
18454     
18455     startDate: -Infinity,
18456     
18457     endDate: Infinity,
18458     
18459     daysOfWeekDisabled: [],
18460     
18461     _events: [],
18462     
18463     singleMode : false,
18464     
18465     UTCDate: function()
18466     {
18467         return new Date(Date.UTC.apply(Date, arguments));
18468     },
18469     
18470     UTCToday: function()
18471     {
18472         var today = new Date();
18473         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18474     },
18475     
18476     getDate: function() {
18477             var d = this.getUTCDate();
18478             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18479     },
18480     
18481     getUTCDate: function() {
18482             return this.date;
18483     },
18484     
18485     setDate: function(d) {
18486             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18487     },
18488     
18489     setUTCDate: function(d) {
18490             this.date = d;
18491             this.setValue(this.formatDate(this.date));
18492     },
18493         
18494     onRender: function(ct, position)
18495     {
18496         
18497         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18498         
18499         this.language = this.language || 'en';
18500         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18501         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18502         
18503         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18504         this.format = this.format || 'm/d/y';
18505         this.isInline = false;
18506         this.isInput = true;
18507         this.component = this.el.select('.add-on', true).first() || false;
18508         this.component = (this.component && this.component.length === 0) ? false : this.component;
18509         this.hasInput = this.component && this.inputEl().length;
18510         
18511         if (typeof(this.minViewMode === 'string')) {
18512             switch (this.minViewMode) {
18513                 case 'months':
18514                     this.minViewMode = 1;
18515                     break;
18516                 case 'years':
18517                     this.minViewMode = 2;
18518                     break;
18519                 default:
18520                     this.minViewMode = 0;
18521                     break;
18522             }
18523         }
18524         
18525         if (typeof(this.viewMode === 'string')) {
18526             switch (this.viewMode) {
18527                 case 'months':
18528                     this.viewMode = 1;
18529                     break;
18530                 case 'years':
18531                     this.viewMode = 2;
18532                     break;
18533                 default:
18534                     this.viewMode = 0;
18535                     break;
18536             }
18537         }
18538                 
18539         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18540         
18541 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18542         
18543         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18544         
18545         this.picker().on('mousedown', this.onMousedown, this);
18546         this.picker().on('click', this.onClick, this);
18547         
18548         this.picker().addClass('datepicker-dropdown');
18549         
18550         this.startViewMode = this.viewMode;
18551         
18552         if(this.singleMode){
18553             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18554                 v.setVisibilityMode(Roo.Element.DISPLAY);
18555                 v.hide();
18556             });
18557             
18558             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18559                 v.setStyle('width', '189px');
18560             });
18561         }
18562         
18563         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18564             if(!this.calendarWeeks){
18565                 v.remove();
18566                 return;
18567             }
18568             
18569             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18570             v.attr('colspan', function(i, val){
18571                 return parseInt(val) + 1;
18572             });
18573         });
18574                         
18575         
18576         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18577         
18578         this.setStartDate(this.startDate);
18579         this.setEndDate(this.endDate);
18580         
18581         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18582         
18583         this.fillDow();
18584         this.fillMonths();
18585         this.update();
18586         this.showMode();
18587         
18588         if(this.isInline) {
18589             this.showPopup();
18590         }
18591     },
18592     
18593     picker : function()
18594     {
18595         return this.pickerEl;
18596 //        return this.el.select('.datepicker', true).first();
18597     },
18598     
18599     fillDow: function()
18600     {
18601         var dowCnt = this.weekStart;
18602         
18603         var dow = {
18604             tag: 'tr',
18605             cn: [
18606                 
18607             ]
18608         };
18609         
18610         if(this.calendarWeeks){
18611             dow.cn.push({
18612                 tag: 'th',
18613                 cls: 'cw',
18614                 html: '&nbsp;'
18615             })
18616         }
18617         
18618         while (dowCnt < this.weekStart + 7) {
18619             dow.cn.push({
18620                 tag: 'th',
18621                 cls: 'dow',
18622                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18623             });
18624         }
18625         
18626         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18627     },
18628     
18629     fillMonths: function()
18630     {    
18631         var i = 0;
18632         var months = this.picker().select('>.datepicker-months td', true).first();
18633         
18634         months.dom.innerHTML = '';
18635         
18636         while (i < 12) {
18637             var month = {
18638                 tag: 'span',
18639                 cls: 'month',
18640                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18641             };
18642             
18643             months.createChild(month);
18644         }
18645         
18646     },
18647     
18648     update: function()
18649     {
18650         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;
18651         
18652         if (this.date < this.startDate) {
18653             this.viewDate = new Date(this.startDate);
18654         } else if (this.date > this.endDate) {
18655             this.viewDate = new Date(this.endDate);
18656         } else {
18657             this.viewDate = new Date(this.date);
18658         }
18659         
18660         this.fill();
18661     },
18662     
18663     fill: function() 
18664     {
18665         var d = new Date(this.viewDate),
18666                 year = d.getUTCFullYear(),
18667                 month = d.getUTCMonth(),
18668                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18669                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18670                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18671                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18672                 currentDate = this.date && this.date.valueOf(),
18673                 today = this.UTCToday();
18674         
18675         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18676         
18677 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18678         
18679 //        this.picker.select('>tfoot th.today').
18680 //                                              .text(dates[this.language].today)
18681 //                                              .toggle(this.todayBtn !== false);
18682     
18683         this.updateNavArrows();
18684         this.fillMonths();
18685                                                 
18686         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18687         
18688         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18689          
18690         prevMonth.setUTCDate(day);
18691         
18692         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18693         
18694         var nextMonth = new Date(prevMonth);
18695         
18696         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18697         
18698         nextMonth = nextMonth.valueOf();
18699         
18700         var fillMonths = false;
18701         
18702         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18703         
18704         while(prevMonth.valueOf() <= nextMonth) {
18705             var clsName = '';
18706             
18707             if (prevMonth.getUTCDay() === this.weekStart) {
18708                 if(fillMonths){
18709                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18710                 }
18711                     
18712                 fillMonths = {
18713                     tag: 'tr',
18714                     cn: []
18715                 };
18716                 
18717                 if(this.calendarWeeks){
18718                     // ISO 8601: First week contains first thursday.
18719                     // ISO also states week starts on Monday, but we can be more abstract here.
18720                     var
18721                     // Start of current week: based on weekstart/current date
18722                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18723                     // Thursday of this week
18724                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18725                     // First Thursday of year, year from thursday
18726                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18727                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18728                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18729                     
18730                     fillMonths.cn.push({
18731                         tag: 'td',
18732                         cls: 'cw',
18733                         html: calWeek
18734                     });
18735                 }
18736             }
18737             
18738             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18739                 clsName += ' old';
18740             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18741                 clsName += ' new';
18742             }
18743             if (this.todayHighlight &&
18744                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18745                 prevMonth.getUTCMonth() == today.getMonth() &&
18746                 prevMonth.getUTCDate() == today.getDate()) {
18747                 clsName += ' today';
18748             }
18749             
18750             if (currentDate && prevMonth.valueOf() === currentDate) {
18751                 clsName += ' active';
18752             }
18753             
18754             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18755                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18756                     clsName += ' disabled';
18757             }
18758             
18759             fillMonths.cn.push({
18760                 tag: 'td',
18761                 cls: 'day ' + clsName,
18762                 html: prevMonth.getDate()
18763             });
18764             
18765             prevMonth.setDate(prevMonth.getDate()+1);
18766         }
18767           
18768         var currentYear = this.date && this.date.getUTCFullYear();
18769         var currentMonth = this.date && this.date.getUTCMonth();
18770         
18771         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18772         
18773         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18774             v.removeClass('active');
18775             
18776             if(currentYear === year && k === currentMonth){
18777                 v.addClass('active');
18778             }
18779             
18780             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18781                 v.addClass('disabled');
18782             }
18783             
18784         });
18785         
18786         
18787         year = parseInt(year/10, 10) * 10;
18788         
18789         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18790         
18791         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18792         
18793         year -= 1;
18794         for (var i = -1; i < 11; i++) {
18795             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18796                 tag: 'span',
18797                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18798                 html: year
18799             });
18800             
18801             year += 1;
18802         }
18803     },
18804     
18805     showMode: function(dir) 
18806     {
18807         if (dir) {
18808             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18809         }
18810         
18811         Roo.each(this.picker().select('>div',true).elements, function(v){
18812             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18813             v.hide();
18814         });
18815         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18816     },
18817     
18818     place: function()
18819     {
18820         if(this.isInline) {
18821             return;
18822         }
18823         
18824         this.picker().removeClass(['bottom', 'top']);
18825         
18826         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18827             /*
18828              * place to the top of element!
18829              *
18830              */
18831             
18832             this.picker().addClass('top');
18833             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18834             
18835             return;
18836         }
18837         
18838         this.picker().addClass('bottom');
18839         
18840         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18841     },
18842     
18843     parseDate : function(value)
18844     {
18845         if(!value || value instanceof Date){
18846             return value;
18847         }
18848         var v = Date.parseDate(value, this.format);
18849         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18850             v = Date.parseDate(value, 'Y-m-d');
18851         }
18852         if(!v && this.altFormats){
18853             if(!this.altFormatsArray){
18854                 this.altFormatsArray = this.altFormats.split("|");
18855             }
18856             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18857                 v = Date.parseDate(value, this.altFormatsArray[i]);
18858             }
18859         }
18860         return v;
18861     },
18862     
18863     formatDate : function(date, fmt)
18864     {   
18865         return (!date || !(date instanceof Date)) ?
18866         date : date.dateFormat(fmt || this.format);
18867     },
18868     
18869     onFocus : function()
18870     {
18871         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18872         this.showPopup();
18873     },
18874     
18875     onBlur : function()
18876     {
18877         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18878         
18879         var d = this.inputEl().getValue();
18880         
18881         this.setValue(d);
18882                 
18883         this.hidePopup();
18884     },
18885     
18886     showPopup : function()
18887     {
18888         this.picker().show();
18889         this.update();
18890         this.place();
18891         
18892         this.fireEvent('showpopup', this, this.date);
18893     },
18894     
18895     hidePopup : function()
18896     {
18897         if(this.isInline) {
18898             return;
18899         }
18900         this.picker().hide();
18901         this.viewMode = this.startViewMode;
18902         this.showMode();
18903         
18904         this.fireEvent('hidepopup', this, this.date);
18905         
18906     },
18907     
18908     onMousedown: function(e)
18909     {
18910         e.stopPropagation();
18911         e.preventDefault();
18912     },
18913     
18914     keyup: function(e)
18915     {
18916         Roo.bootstrap.DateField.superclass.keyup.call(this);
18917         this.update();
18918     },
18919
18920     setValue: function(v)
18921     {
18922         if(this.fireEvent('beforeselect', this, v) !== false){
18923             var d = new Date(this.parseDate(v) ).clearTime();
18924         
18925             if(isNaN(d.getTime())){
18926                 this.date = this.viewDate = '';
18927                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18928                 return;
18929             }
18930
18931             v = this.formatDate(d);
18932
18933             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18934
18935             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18936
18937             this.update();
18938
18939             this.fireEvent('select', this, this.date);
18940         }
18941     },
18942     
18943     getValue: function()
18944     {
18945         return this.formatDate(this.date);
18946     },
18947     
18948     fireKey: function(e)
18949     {
18950         if (!this.picker().isVisible()){
18951             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18952                 this.showPopup();
18953             }
18954             return;
18955         }
18956         
18957         var dateChanged = false,
18958         dir, day, month,
18959         newDate, newViewDate;
18960         
18961         switch(e.keyCode){
18962             case 27: // escape
18963                 this.hidePopup();
18964                 e.preventDefault();
18965                 break;
18966             case 37: // left
18967             case 39: // right
18968                 if (!this.keyboardNavigation) {
18969                     break;
18970                 }
18971                 dir = e.keyCode == 37 ? -1 : 1;
18972                 
18973                 if (e.ctrlKey){
18974                     newDate = this.moveYear(this.date, dir);
18975                     newViewDate = this.moveYear(this.viewDate, dir);
18976                 } else if (e.shiftKey){
18977                     newDate = this.moveMonth(this.date, dir);
18978                     newViewDate = this.moveMonth(this.viewDate, dir);
18979                 } else {
18980                     newDate = new Date(this.date);
18981                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18982                     newViewDate = new Date(this.viewDate);
18983                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18984                 }
18985                 if (this.dateWithinRange(newDate)){
18986                     this.date = newDate;
18987                     this.viewDate = newViewDate;
18988                     this.setValue(this.formatDate(this.date));
18989 //                    this.update();
18990                     e.preventDefault();
18991                     dateChanged = true;
18992                 }
18993                 break;
18994             case 38: // up
18995             case 40: // down
18996                 if (!this.keyboardNavigation) {
18997                     break;
18998                 }
18999                 dir = e.keyCode == 38 ? -1 : 1;
19000                 if (e.ctrlKey){
19001                     newDate = this.moveYear(this.date, dir);
19002                     newViewDate = this.moveYear(this.viewDate, dir);
19003                 } else if (e.shiftKey){
19004                     newDate = this.moveMonth(this.date, dir);
19005                     newViewDate = this.moveMonth(this.viewDate, dir);
19006                 } else {
19007                     newDate = new Date(this.date);
19008                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19009                     newViewDate = new Date(this.viewDate);
19010                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19011                 }
19012                 if (this.dateWithinRange(newDate)){
19013                     this.date = newDate;
19014                     this.viewDate = newViewDate;
19015                     this.setValue(this.formatDate(this.date));
19016 //                    this.update();
19017                     e.preventDefault();
19018                     dateChanged = true;
19019                 }
19020                 break;
19021             case 13: // enter
19022                 this.setValue(this.formatDate(this.date));
19023                 this.hidePopup();
19024                 e.preventDefault();
19025                 break;
19026             case 9: // tab
19027                 this.setValue(this.formatDate(this.date));
19028                 this.hidePopup();
19029                 break;
19030             case 16: // shift
19031             case 17: // ctrl
19032             case 18: // alt
19033                 break;
19034             default :
19035                 this.hide();
19036                 
19037         }
19038     },
19039     
19040     
19041     onClick: function(e) 
19042     {
19043         e.stopPropagation();
19044         e.preventDefault();
19045         
19046         var target = e.getTarget();
19047         
19048         if(target.nodeName.toLowerCase() === 'i'){
19049             target = Roo.get(target).dom.parentNode;
19050         }
19051         
19052         var nodeName = target.nodeName;
19053         var className = target.className;
19054         var html = target.innerHTML;
19055         //Roo.log(nodeName);
19056         
19057         switch(nodeName.toLowerCase()) {
19058             case 'th':
19059                 switch(className) {
19060                     case 'switch':
19061                         this.showMode(1);
19062                         break;
19063                     case 'prev':
19064                     case 'next':
19065                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19066                         switch(this.viewMode){
19067                                 case 0:
19068                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19069                                         break;
19070                                 case 1:
19071                                 case 2:
19072                                         this.viewDate = this.moveYear(this.viewDate, dir);
19073                                         break;
19074                         }
19075                         this.fill();
19076                         break;
19077                     case 'today':
19078                         var date = new Date();
19079                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19080 //                        this.fill()
19081                         this.setValue(this.formatDate(this.date));
19082                         
19083                         this.hidePopup();
19084                         break;
19085                 }
19086                 break;
19087             case 'span':
19088                 if (className.indexOf('disabled') < 0) {
19089                     this.viewDate.setUTCDate(1);
19090                     if (className.indexOf('month') > -1) {
19091                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19092                     } else {
19093                         var year = parseInt(html, 10) || 0;
19094                         this.viewDate.setUTCFullYear(year);
19095                         
19096                     }
19097                     
19098                     if(this.singleMode){
19099                         this.setValue(this.formatDate(this.viewDate));
19100                         this.hidePopup();
19101                         return;
19102                     }
19103                     
19104                     this.showMode(-1);
19105                     this.fill();
19106                 }
19107                 break;
19108                 
19109             case 'td':
19110                 //Roo.log(className);
19111                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19112                     var day = parseInt(html, 10) || 1;
19113                     var year = this.viewDate.getUTCFullYear(),
19114                         month = this.viewDate.getUTCMonth();
19115
19116                     if (className.indexOf('old') > -1) {
19117                         if(month === 0 ){
19118                             month = 11;
19119                             year -= 1;
19120                         }else{
19121                             month -= 1;
19122                         }
19123                     } else if (className.indexOf('new') > -1) {
19124                         if (month == 11) {
19125                             month = 0;
19126                             year += 1;
19127                         } else {
19128                             month += 1;
19129                         }
19130                     }
19131                     //Roo.log([year,month,day]);
19132                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19133                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19134 //                    this.fill();
19135                     //Roo.log(this.formatDate(this.date));
19136                     this.setValue(this.formatDate(this.date));
19137                     this.hidePopup();
19138                 }
19139                 break;
19140         }
19141     },
19142     
19143     setStartDate: function(startDate)
19144     {
19145         this.startDate = startDate || -Infinity;
19146         if (this.startDate !== -Infinity) {
19147             this.startDate = this.parseDate(this.startDate);
19148         }
19149         this.update();
19150         this.updateNavArrows();
19151     },
19152
19153     setEndDate: function(endDate)
19154     {
19155         this.endDate = endDate || Infinity;
19156         if (this.endDate !== Infinity) {
19157             this.endDate = this.parseDate(this.endDate);
19158         }
19159         this.update();
19160         this.updateNavArrows();
19161     },
19162     
19163     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19164     {
19165         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19166         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19167             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19168         }
19169         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19170             return parseInt(d, 10);
19171         });
19172         this.update();
19173         this.updateNavArrows();
19174     },
19175     
19176     updateNavArrows: function() 
19177     {
19178         if(this.singleMode){
19179             return;
19180         }
19181         
19182         var d = new Date(this.viewDate),
19183         year = d.getUTCFullYear(),
19184         month = d.getUTCMonth();
19185         
19186         Roo.each(this.picker().select('.prev', true).elements, function(v){
19187             v.show();
19188             switch (this.viewMode) {
19189                 case 0:
19190
19191                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19192                         v.hide();
19193                     }
19194                     break;
19195                 case 1:
19196                 case 2:
19197                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19198                         v.hide();
19199                     }
19200                     break;
19201             }
19202         });
19203         
19204         Roo.each(this.picker().select('.next', true).elements, function(v){
19205             v.show();
19206             switch (this.viewMode) {
19207                 case 0:
19208
19209                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19210                         v.hide();
19211                     }
19212                     break;
19213                 case 1:
19214                 case 2:
19215                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19216                         v.hide();
19217                     }
19218                     break;
19219             }
19220         })
19221     },
19222     
19223     moveMonth: function(date, dir)
19224     {
19225         if (!dir) {
19226             return date;
19227         }
19228         var new_date = new Date(date.valueOf()),
19229         day = new_date.getUTCDate(),
19230         month = new_date.getUTCMonth(),
19231         mag = Math.abs(dir),
19232         new_month, test;
19233         dir = dir > 0 ? 1 : -1;
19234         if (mag == 1){
19235             test = dir == -1
19236             // If going back one month, make sure month is not current month
19237             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19238             ? function(){
19239                 return new_date.getUTCMonth() == month;
19240             }
19241             // If going forward one month, make sure month is as expected
19242             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19243             : function(){
19244                 return new_date.getUTCMonth() != new_month;
19245             };
19246             new_month = month + dir;
19247             new_date.setUTCMonth(new_month);
19248             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19249             if (new_month < 0 || new_month > 11) {
19250                 new_month = (new_month + 12) % 12;
19251             }
19252         } else {
19253             // For magnitudes >1, move one month at a time...
19254             for (var i=0; i<mag; i++) {
19255                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19256                 new_date = this.moveMonth(new_date, dir);
19257             }
19258             // ...then reset the day, keeping it in the new month
19259             new_month = new_date.getUTCMonth();
19260             new_date.setUTCDate(day);
19261             test = function(){
19262                 return new_month != new_date.getUTCMonth();
19263             };
19264         }
19265         // Common date-resetting loop -- if date is beyond end of month, make it
19266         // end of month
19267         while (test()){
19268             new_date.setUTCDate(--day);
19269             new_date.setUTCMonth(new_month);
19270         }
19271         return new_date;
19272     },
19273
19274     moveYear: function(date, dir)
19275     {
19276         return this.moveMonth(date, dir*12);
19277     },
19278
19279     dateWithinRange: function(date)
19280     {
19281         return date >= this.startDate && date <= this.endDate;
19282     },
19283
19284     
19285     remove: function() 
19286     {
19287         this.picker().remove();
19288     },
19289     
19290     validateValue : function(value)
19291     {
19292         if(this.getVisibilityEl().hasClass('hidden')){
19293             return true;
19294         }
19295         
19296         if(value.length < 1)  {
19297             if(this.allowBlank){
19298                 return true;
19299             }
19300             return false;
19301         }
19302         
19303         if(value.length < this.minLength){
19304             return false;
19305         }
19306         if(value.length > this.maxLength){
19307             return false;
19308         }
19309         if(this.vtype){
19310             var vt = Roo.form.VTypes;
19311             if(!vt[this.vtype](value, this)){
19312                 return false;
19313             }
19314         }
19315         if(typeof this.validator == "function"){
19316             var msg = this.validator(value);
19317             if(msg !== true){
19318                 return false;
19319             }
19320         }
19321         
19322         if(this.regex && !this.regex.test(value)){
19323             return false;
19324         }
19325         
19326         if(typeof(this.parseDate(value)) == 'undefined'){
19327             return false;
19328         }
19329         
19330         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19331             return false;
19332         }      
19333         
19334         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19335             return false;
19336         } 
19337         
19338         
19339         return true;
19340     },
19341     
19342     reset : function()
19343     {
19344         this.date = this.viewDate = '';
19345         
19346         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19347     }
19348    
19349 });
19350
19351 Roo.apply(Roo.bootstrap.DateField,  {
19352     
19353     head : {
19354         tag: 'thead',
19355         cn: [
19356         {
19357             tag: 'tr',
19358             cn: [
19359             {
19360                 tag: 'th',
19361                 cls: 'prev',
19362                 html: '<i class="fa fa-arrow-left"/>'
19363             },
19364             {
19365                 tag: 'th',
19366                 cls: 'switch',
19367                 colspan: '5'
19368             },
19369             {
19370                 tag: 'th',
19371                 cls: 'next',
19372                 html: '<i class="fa fa-arrow-right"/>'
19373             }
19374
19375             ]
19376         }
19377         ]
19378     },
19379     
19380     content : {
19381         tag: 'tbody',
19382         cn: [
19383         {
19384             tag: 'tr',
19385             cn: [
19386             {
19387                 tag: 'td',
19388                 colspan: '7'
19389             }
19390             ]
19391         }
19392         ]
19393     },
19394     
19395     footer : {
19396         tag: 'tfoot',
19397         cn: [
19398         {
19399             tag: 'tr',
19400             cn: [
19401             {
19402                 tag: 'th',
19403                 colspan: '7',
19404                 cls: 'today'
19405             }
19406                     
19407             ]
19408         }
19409         ]
19410     },
19411     
19412     dates:{
19413         en: {
19414             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19415             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19416             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19417             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19418             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19419             today: "Today"
19420         }
19421     },
19422     
19423     modes: [
19424     {
19425         clsName: 'days',
19426         navFnc: 'Month',
19427         navStep: 1
19428     },
19429     {
19430         clsName: 'months',
19431         navFnc: 'FullYear',
19432         navStep: 1
19433     },
19434     {
19435         clsName: 'years',
19436         navFnc: 'FullYear',
19437         navStep: 10
19438     }]
19439 });
19440
19441 Roo.apply(Roo.bootstrap.DateField,  {
19442   
19443     template : {
19444         tag: 'div',
19445         cls: 'datepicker dropdown-menu roo-dynamic',
19446         cn: [
19447         {
19448             tag: 'div',
19449             cls: 'datepicker-days',
19450             cn: [
19451             {
19452                 tag: 'table',
19453                 cls: 'table-condensed',
19454                 cn:[
19455                 Roo.bootstrap.DateField.head,
19456                 {
19457                     tag: 'tbody'
19458                 },
19459                 Roo.bootstrap.DateField.footer
19460                 ]
19461             }
19462             ]
19463         },
19464         {
19465             tag: 'div',
19466             cls: 'datepicker-months',
19467             cn: [
19468             {
19469                 tag: 'table',
19470                 cls: 'table-condensed',
19471                 cn:[
19472                 Roo.bootstrap.DateField.head,
19473                 Roo.bootstrap.DateField.content,
19474                 Roo.bootstrap.DateField.footer
19475                 ]
19476             }
19477             ]
19478         },
19479         {
19480             tag: 'div',
19481             cls: 'datepicker-years',
19482             cn: [
19483             {
19484                 tag: 'table',
19485                 cls: 'table-condensed',
19486                 cn:[
19487                 Roo.bootstrap.DateField.head,
19488                 Roo.bootstrap.DateField.content,
19489                 Roo.bootstrap.DateField.footer
19490                 ]
19491             }
19492             ]
19493         }
19494         ]
19495     }
19496 });
19497
19498  
19499
19500  /*
19501  * - LGPL
19502  *
19503  * TimeField
19504  * 
19505  */
19506
19507 /**
19508  * @class Roo.bootstrap.TimeField
19509  * @extends Roo.bootstrap.Input
19510  * Bootstrap DateField class
19511  * 
19512  * 
19513  * @constructor
19514  * Create a new TimeField
19515  * @param {Object} config The config object
19516  */
19517
19518 Roo.bootstrap.TimeField = function(config){
19519     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19520     this.addEvents({
19521             /**
19522              * @event show
19523              * Fires when this field show.
19524              * @param {Roo.bootstrap.DateField} thisthis
19525              * @param {Mixed} date The date value
19526              */
19527             show : true,
19528             /**
19529              * @event show
19530              * Fires when this field hide.
19531              * @param {Roo.bootstrap.DateField} this
19532              * @param {Mixed} date The date value
19533              */
19534             hide : true,
19535             /**
19536              * @event select
19537              * Fires when select a date.
19538              * @param {Roo.bootstrap.DateField} this
19539              * @param {Mixed} date The date value
19540              */
19541             select : true
19542         });
19543 };
19544
19545 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19546     
19547     /**
19548      * @cfg {String} format
19549      * The default time format string which can be overriden for localization support.  The format must be
19550      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19551      */
19552     format : "H:i",
19553        
19554     onRender: function(ct, position)
19555     {
19556         
19557         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19558                 
19559         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19560         
19561         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19562         
19563         this.pop = this.picker().select('>.datepicker-time',true).first();
19564         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19565         
19566         this.picker().on('mousedown', this.onMousedown, this);
19567         this.picker().on('click', this.onClick, this);
19568         
19569         this.picker().addClass('datepicker-dropdown');
19570     
19571         this.fillTime();
19572         this.update();
19573             
19574         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19575         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19576         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19577         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19578         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19579         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19580
19581     },
19582     
19583     fireKey: function(e){
19584         if (!this.picker().isVisible()){
19585             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19586                 this.show();
19587             }
19588             return;
19589         }
19590
19591         e.preventDefault();
19592         
19593         switch(e.keyCode){
19594             case 27: // escape
19595                 this.hide();
19596                 break;
19597             case 37: // left
19598             case 39: // right
19599                 this.onTogglePeriod();
19600                 break;
19601             case 38: // up
19602                 this.onIncrementMinutes();
19603                 break;
19604             case 40: // down
19605                 this.onDecrementMinutes();
19606                 break;
19607             case 13: // enter
19608             case 9: // tab
19609                 this.setTime();
19610                 break;
19611         }
19612     },
19613     
19614     onClick: function(e) {
19615         e.stopPropagation();
19616         e.preventDefault();
19617     },
19618     
19619     picker : function()
19620     {
19621         return this.el.select('.datepicker', true).first();
19622     },
19623     
19624     fillTime: function()
19625     {    
19626         var time = this.pop.select('tbody', true).first();
19627         
19628         time.dom.innerHTML = '';
19629         
19630         time.createChild({
19631             tag: 'tr',
19632             cn: [
19633                 {
19634                     tag: 'td',
19635                     cn: [
19636                         {
19637                             tag: 'a',
19638                             href: '#',
19639                             cls: 'btn',
19640                             cn: [
19641                                 {
19642                                     tag: 'span',
19643                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19644                                 }
19645                             ]
19646                         } 
19647                     ]
19648                 },
19649                 {
19650                     tag: 'td',
19651                     cls: 'separator'
19652                 },
19653                 {
19654                     tag: 'td',
19655                     cn: [
19656                         {
19657                             tag: 'a',
19658                             href: '#',
19659                             cls: 'btn',
19660                             cn: [
19661                                 {
19662                                     tag: 'span',
19663                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19664                                 }
19665                             ]
19666                         }
19667                     ]
19668                 },
19669                 {
19670                     tag: 'td',
19671                     cls: 'separator'
19672                 }
19673             ]
19674         });
19675         
19676         time.createChild({
19677             tag: 'tr',
19678             cn: [
19679                 {
19680                     tag: 'td',
19681                     cn: [
19682                         {
19683                             tag: 'span',
19684                             cls: 'timepicker-hour',
19685                             html: '00'
19686                         }  
19687                     ]
19688                 },
19689                 {
19690                     tag: 'td',
19691                     cls: 'separator',
19692                     html: ':'
19693                 },
19694                 {
19695                     tag: 'td',
19696                     cn: [
19697                         {
19698                             tag: 'span',
19699                             cls: 'timepicker-minute',
19700                             html: '00'
19701                         }  
19702                     ]
19703                 },
19704                 {
19705                     tag: 'td',
19706                     cls: 'separator'
19707                 },
19708                 {
19709                     tag: 'td',
19710                     cn: [
19711                         {
19712                             tag: 'button',
19713                             type: 'button',
19714                             cls: 'btn btn-primary period',
19715                             html: 'AM'
19716                             
19717                         }
19718                     ]
19719                 }
19720             ]
19721         });
19722         
19723         time.createChild({
19724             tag: 'tr',
19725             cn: [
19726                 {
19727                     tag: 'td',
19728                     cn: [
19729                         {
19730                             tag: 'a',
19731                             href: '#',
19732                             cls: 'btn',
19733                             cn: [
19734                                 {
19735                                     tag: 'span',
19736                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19737                                 }
19738                             ]
19739                         }
19740                     ]
19741                 },
19742                 {
19743                     tag: 'td',
19744                     cls: 'separator'
19745                 },
19746                 {
19747                     tag: 'td',
19748                     cn: [
19749                         {
19750                             tag: 'a',
19751                             href: '#',
19752                             cls: 'btn',
19753                             cn: [
19754                                 {
19755                                     tag: 'span',
19756                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19757                                 }
19758                             ]
19759                         }
19760                     ]
19761                 },
19762                 {
19763                     tag: 'td',
19764                     cls: 'separator'
19765                 }
19766             ]
19767         });
19768         
19769     },
19770     
19771     update: function()
19772     {
19773         
19774         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19775         
19776         this.fill();
19777     },
19778     
19779     fill: function() 
19780     {
19781         var hours = this.time.getHours();
19782         var minutes = this.time.getMinutes();
19783         var period = 'AM';
19784         
19785         if(hours > 11){
19786             period = 'PM';
19787         }
19788         
19789         if(hours == 0){
19790             hours = 12;
19791         }
19792         
19793         
19794         if(hours > 12){
19795             hours = hours - 12;
19796         }
19797         
19798         if(hours < 10){
19799             hours = '0' + hours;
19800         }
19801         
19802         if(minutes < 10){
19803             minutes = '0' + minutes;
19804         }
19805         
19806         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19807         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19808         this.pop.select('button', true).first().dom.innerHTML = period;
19809         
19810     },
19811     
19812     place: function()
19813     {   
19814         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19815         
19816         var cls = ['bottom'];
19817         
19818         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19819             cls.pop();
19820             cls.push('top');
19821         }
19822         
19823         cls.push('right');
19824         
19825         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19826             cls.pop();
19827             cls.push('left');
19828         }
19829         
19830         this.picker().addClass(cls.join('-'));
19831         
19832         var _this = this;
19833         
19834         Roo.each(cls, function(c){
19835             if(c == 'bottom'){
19836                 _this.picker().setTop(_this.inputEl().getHeight());
19837                 return;
19838             }
19839             if(c == 'top'){
19840                 _this.picker().setTop(0 - _this.picker().getHeight());
19841                 return;
19842             }
19843             
19844             if(c == 'left'){
19845                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19846                 return;
19847             }
19848             if(c == 'right'){
19849                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19850                 return;
19851             }
19852         });
19853         
19854     },
19855   
19856     onFocus : function()
19857     {
19858         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19859         this.show();
19860     },
19861     
19862     onBlur : function()
19863     {
19864         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19865         this.hide();
19866     },
19867     
19868     show : function()
19869     {
19870         this.picker().show();
19871         this.pop.show();
19872         this.update();
19873         this.place();
19874         
19875         this.fireEvent('show', this, this.date);
19876     },
19877     
19878     hide : function()
19879     {
19880         this.picker().hide();
19881         this.pop.hide();
19882         
19883         this.fireEvent('hide', this, this.date);
19884     },
19885     
19886     setTime : function()
19887     {
19888         this.hide();
19889         this.setValue(this.time.format(this.format));
19890         
19891         this.fireEvent('select', this, this.date);
19892         
19893         
19894     },
19895     
19896     onMousedown: function(e){
19897         e.stopPropagation();
19898         e.preventDefault();
19899     },
19900     
19901     onIncrementHours: function()
19902     {
19903         Roo.log('onIncrementHours');
19904         this.time = this.time.add(Date.HOUR, 1);
19905         this.update();
19906         
19907     },
19908     
19909     onDecrementHours: function()
19910     {
19911         Roo.log('onDecrementHours');
19912         this.time = this.time.add(Date.HOUR, -1);
19913         this.update();
19914     },
19915     
19916     onIncrementMinutes: function()
19917     {
19918         Roo.log('onIncrementMinutes');
19919         this.time = this.time.add(Date.MINUTE, 1);
19920         this.update();
19921     },
19922     
19923     onDecrementMinutes: function()
19924     {
19925         Roo.log('onDecrementMinutes');
19926         this.time = this.time.add(Date.MINUTE, -1);
19927         this.update();
19928     },
19929     
19930     onTogglePeriod: function()
19931     {
19932         Roo.log('onTogglePeriod');
19933         this.time = this.time.add(Date.HOUR, 12);
19934         this.update();
19935     }
19936     
19937    
19938 });
19939
19940 Roo.apply(Roo.bootstrap.TimeField,  {
19941     
19942     content : {
19943         tag: 'tbody',
19944         cn: [
19945             {
19946                 tag: 'tr',
19947                 cn: [
19948                 {
19949                     tag: 'td',
19950                     colspan: '7'
19951                 }
19952                 ]
19953             }
19954         ]
19955     },
19956     
19957     footer : {
19958         tag: 'tfoot',
19959         cn: [
19960             {
19961                 tag: 'tr',
19962                 cn: [
19963                 {
19964                     tag: 'th',
19965                     colspan: '7',
19966                     cls: '',
19967                     cn: [
19968                         {
19969                             tag: 'button',
19970                             cls: 'btn btn-info ok',
19971                             html: 'OK'
19972                         }
19973                     ]
19974                 }
19975
19976                 ]
19977             }
19978         ]
19979     }
19980 });
19981
19982 Roo.apply(Roo.bootstrap.TimeField,  {
19983   
19984     template : {
19985         tag: 'div',
19986         cls: 'datepicker dropdown-menu',
19987         cn: [
19988             {
19989                 tag: 'div',
19990                 cls: 'datepicker-time',
19991                 cn: [
19992                 {
19993                     tag: 'table',
19994                     cls: 'table-condensed',
19995                     cn:[
19996                     Roo.bootstrap.TimeField.content,
19997                     Roo.bootstrap.TimeField.footer
19998                     ]
19999                 }
20000                 ]
20001             }
20002         ]
20003     }
20004 });
20005
20006  
20007
20008  /*
20009  * - LGPL
20010  *
20011  * MonthField
20012  * 
20013  */
20014
20015 /**
20016  * @class Roo.bootstrap.MonthField
20017  * @extends Roo.bootstrap.Input
20018  * Bootstrap MonthField class
20019  * 
20020  * @cfg {String} language default en
20021  * 
20022  * @constructor
20023  * Create a new MonthField
20024  * @param {Object} config The config object
20025  */
20026
20027 Roo.bootstrap.MonthField = function(config){
20028     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20029     
20030     this.addEvents({
20031         /**
20032          * @event show
20033          * Fires when this field show.
20034          * @param {Roo.bootstrap.MonthField} this
20035          * @param {Mixed} date The date value
20036          */
20037         show : true,
20038         /**
20039          * @event show
20040          * Fires when this field hide.
20041          * @param {Roo.bootstrap.MonthField} this
20042          * @param {Mixed} date The date value
20043          */
20044         hide : true,
20045         /**
20046          * @event select
20047          * Fires when select a date.
20048          * @param {Roo.bootstrap.MonthField} this
20049          * @param {String} oldvalue The old value
20050          * @param {String} newvalue The new value
20051          */
20052         select : true
20053     });
20054 };
20055
20056 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20057     
20058     onRender: function(ct, position)
20059     {
20060         
20061         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20062         
20063         this.language = this.language || 'en';
20064         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20065         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20066         
20067         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20068         this.isInline = false;
20069         this.isInput = true;
20070         this.component = this.el.select('.add-on', true).first() || false;
20071         this.component = (this.component && this.component.length === 0) ? false : this.component;
20072         this.hasInput = this.component && this.inputEL().length;
20073         
20074         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20075         
20076         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20077         
20078         this.picker().on('mousedown', this.onMousedown, this);
20079         this.picker().on('click', this.onClick, this);
20080         
20081         this.picker().addClass('datepicker-dropdown');
20082         
20083         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20084             v.setStyle('width', '189px');
20085         });
20086         
20087         this.fillMonths();
20088         
20089         this.update();
20090         
20091         if(this.isInline) {
20092             this.show();
20093         }
20094         
20095     },
20096     
20097     setValue: function(v, suppressEvent)
20098     {   
20099         var o = this.getValue();
20100         
20101         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20102         
20103         this.update();
20104
20105         if(suppressEvent !== true){
20106             this.fireEvent('select', this, o, v);
20107         }
20108         
20109     },
20110     
20111     getValue: function()
20112     {
20113         return this.value;
20114     },
20115     
20116     onClick: function(e) 
20117     {
20118         e.stopPropagation();
20119         e.preventDefault();
20120         
20121         var target = e.getTarget();
20122         
20123         if(target.nodeName.toLowerCase() === 'i'){
20124             target = Roo.get(target).dom.parentNode;
20125         }
20126         
20127         var nodeName = target.nodeName;
20128         var className = target.className;
20129         var html = target.innerHTML;
20130         
20131         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20132             return;
20133         }
20134         
20135         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20136         
20137         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20138         
20139         this.hide();
20140                         
20141     },
20142     
20143     picker : function()
20144     {
20145         return this.pickerEl;
20146     },
20147     
20148     fillMonths: function()
20149     {    
20150         var i = 0;
20151         var months = this.picker().select('>.datepicker-months td', true).first();
20152         
20153         months.dom.innerHTML = '';
20154         
20155         while (i < 12) {
20156             var month = {
20157                 tag: 'span',
20158                 cls: 'month',
20159                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20160             };
20161             
20162             months.createChild(month);
20163         }
20164         
20165     },
20166     
20167     update: function()
20168     {
20169         var _this = this;
20170         
20171         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20172             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20173         }
20174         
20175         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20176             e.removeClass('active');
20177             
20178             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20179                 e.addClass('active');
20180             }
20181         })
20182     },
20183     
20184     place: function()
20185     {
20186         if(this.isInline) {
20187             return;
20188         }
20189         
20190         this.picker().removeClass(['bottom', 'top']);
20191         
20192         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20193             /*
20194              * place to the top of element!
20195              *
20196              */
20197             
20198             this.picker().addClass('top');
20199             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20200             
20201             return;
20202         }
20203         
20204         this.picker().addClass('bottom');
20205         
20206         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20207     },
20208     
20209     onFocus : function()
20210     {
20211         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20212         this.show();
20213     },
20214     
20215     onBlur : function()
20216     {
20217         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20218         
20219         var d = this.inputEl().getValue();
20220         
20221         this.setValue(d);
20222                 
20223         this.hide();
20224     },
20225     
20226     show : function()
20227     {
20228         this.picker().show();
20229         this.picker().select('>.datepicker-months', true).first().show();
20230         this.update();
20231         this.place();
20232         
20233         this.fireEvent('show', this, this.date);
20234     },
20235     
20236     hide : function()
20237     {
20238         if(this.isInline) {
20239             return;
20240         }
20241         this.picker().hide();
20242         this.fireEvent('hide', this, this.date);
20243         
20244     },
20245     
20246     onMousedown: function(e)
20247     {
20248         e.stopPropagation();
20249         e.preventDefault();
20250     },
20251     
20252     keyup: function(e)
20253     {
20254         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20255         this.update();
20256     },
20257
20258     fireKey: function(e)
20259     {
20260         if (!this.picker().isVisible()){
20261             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20262                 this.show();
20263             }
20264             return;
20265         }
20266         
20267         var dir;
20268         
20269         switch(e.keyCode){
20270             case 27: // escape
20271                 this.hide();
20272                 e.preventDefault();
20273                 break;
20274             case 37: // left
20275             case 39: // right
20276                 dir = e.keyCode == 37 ? -1 : 1;
20277                 
20278                 this.vIndex = this.vIndex + dir;
20279                 
20280                 if(this.vIndex < 0){
20281                     this.vIndex = 0;
20282                 }
20283                 
20284                 if(this.vIndex > 11){
20285                     this.vIndex = 11;
20286                 }
20287                 
20288                 if(isNaN(this.vIndex)){
20289                     this.vIndex = 0;
20290                 }
20291                 
20292                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20293                 
20294                 break;
20295             case 38: // up
20296             case 40: // down
20297                 
20298                 dir = e.keyCode == 38 ? -1 : 1;
20299                 
20300                 this.vIndex = this.vIndex + dir * 4;
20301                 
20302                 if(this.vIndex < 0){
20303                     this.vIndex = 0;
20304                 }
20305                 
20306                 if(this.vIndex > 11){
20307                     this.vIndex = 11;
20308                 }
20309                 
20310                 if(isNaN(this.vIndex)){
20311                     this.vIndex = 0;
20312                 }
20313                 
20314                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20315                 break;
20316                 
20317             case 13: // enter
20318                 
20319                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20320                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20321                 }
20322                 
20323                 this.hide();
20324                 e.preventDefault();
20325                 break;
20326             case 9: // tab
20327                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20328                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20329                 }
20330                 this.hide();
20331                 break;
20332             case 16: // shift
20333             case 17: // ctrl
20334             case 18: // alt
20335                 break;
20336             default :
20337                 this.hide();
20338                 
20339         }
20340     },
20341     
20342     remove: function() 
20343     {
20344         this.picker().remove();
20345     }
20346    
20347 });
20348
20349 Roo.apply(Roo.bootstrap.MonthField,  {
20350     
20351     content : {
20352         tag: 'tbody',
20353         cn: [
20354         {
20355             tag: 'tr',
20356             cn: [
20357             {
20358                 tag: 'td',
20359                 colspan: '7'
20360             }
20361             ]
20362         }
20363         ]
20364     },
20365     
20366     dates:{
20367         en: {
20368             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20369             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20370         }
20371     }
20372 });
20373
20374 Roo.apply(Roo.bootstrap.MonthField,  {
20375   
20376     template : {
20377         tag: 'div',
20378         cls: 'datepicker dropdown-menu roo-dynamic',
20379         cn: [
20380             {
20381                 tag: 'div',
20382                 cls: 'datepicker-months',
20383                 cn: [
20384                 {
20385                     tag: 'table',
20386                     cls: 'table-condensed',
20387                     cn:[
20388                         Roo.bootstrap.DateField.content
20389                     ]
20390                 }
20391                 ]
20392             }
20393         ]
20394     }
20395 });
20396
20397  
20398
20399  
20400  /*
20401  * - LGPL
20402  *
20403  * CheckBox
20404  * 
20405  */
20406
20407 /**
20408  * @class Roo.bootstrap.CheckBox
20409  * @extends Roo.bootstrap.Input
20410  * Bootstrap CheckBox class
20411  * 
20412  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20413  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20414  * @cfg {String} boxLabel The text that appears beside the checkbox
20415  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20416  * @cfg {Boolean} checked initnal the element
20417  * @cfg {Boolean} inline inline the element (default false)
20418  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20419  * @cfg {String} tooltip label tooltip
20420  * 
20421  * @constructor
20422  * Create a new CheckBox
20423  * @param {Object} config The config object
20424  */
20425
20426 Roo.bootstrap.CheckBox = function(config){
20427     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20428    
20429     this.addEvents({
20430         /**
20431         * @event check
20432         * Fires when the element is checked or unchecked.
20433         * @param {Roo.bootstrap.CheckBox} this This input
20434         * @param {Boolean} checked The new checked value
20435         */
20436        check : true,
20437        /**
20438         * @event click
20439         * Fires when the element is click.
20440         * @param {Roo.bootstrap.CheckBox} this This input
20441         */
20442        click : true
20443     });
20444     
20445 };
20446
20447 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20448   
20449     inputType: 'checkbox',
20450     inputValue: 1,
20451     valueOff: 0,
20452     boxLabel: false,
20453     checked: false,
20454     weight : false,
20455     inline: false,
20456     tooltip : '',
20457     
20458     getAutoCreate : function()
20459     {
20460         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20461         
20462         var id = Roo.id();
20463         
20464         var cfg = {};
20465         
20466         cfg.cls = 'form-group ' + this.inputType; //input-group
20467         
20468         if(this.inline){
20469             cfg.cls += ' ' + this.inputType + '-inline';
20470         }
20471         
20472         var input =  {
20473             tag: 'input',
20474             id : id,
20475             type : this.inputType,
20476             value : this.inputValue,
20477             cls : 'roo-' + this.inputType, //'form-box',
20478             placeholder : this.placeholder || ''
20479             
20480         };
20481         
20482         if(this.inputType != 'radio'){
20483             var hidden =  {
20484                 tag: 'input',
20485                 type : 'hidden',
20486                 cls : 'roo-hidden-value',
20487                 value : this.checked ? this.inputValue : this.valueOff
20488             };
20489         }
20490         
20491             
20492         if (this.weight) { // Validity check?
20493             cfg.cls += " " + this.inputType + "-" + this.weight;
20494         }
20495         
20496         if (this.disabled) {
20497             input.disabled=true;
20498         }
20499         
20500         if(this.checked){
20501             input.checked = this.checked;
20502         }
20503         
20504         if (this.name) {
20505             
20506             input.name = this.name;
20507             
20508             if(this.inputType != 'radio'){
20509                 hidden.name = this.name;
20510                 input.name = '_hidden_' + this.name;
20511             }
20512         }
20513         
20514         if (this.size) {
20515             input.cls += ' input-' + this.size;
20516         }
20517         
20518         var settings=this;
20519         
20520         ['xs','sm','md','lg'].map(function(size){
20521             if (settings[size]) {
20522                 cfg.cls += ' col-' + size + '-' + settings[size];
20523             }
20524         });
20525         
20526         var inputblock = input;
20527          
20528         if (this.before || this.after) {
20529             
20530             inputblock = {
20531                 cls : 'input-group',
20532                 cn :  [] 
20533             };
20534             
20535             if (this.before) {
20536                 inputblock.cn.push({
20537                     tag :'span',
20538                     cls : 'input-group-addon',
20539                     html : this.before
20540                 });
20541             }
20542             
20543             inputblock.cn.push(input);
20544             
20545             if(this.inputType != 'radio'){
20546                 inputblock.cn.push(hidden);
20547             }
20548             
20549             if (this.after) {
20550                 inputblock.cn.push({
20551                     tag :'span',
20552                     cls : 'input-group-addon',
20553                     html : this.after
20554                 });
20555             }
20556             
20557         }
20558         
20559         if (align ==='left' && this.fieldLabel.length) {
20560 //                Roo.log("left and has label");
20561             cfg.cn = [
20562                 {
20563                     tag: 'label',
20564                     'for' :  id,
20565                     cls : 'control-label',
20566                     html : this.fieldLabel
20567                 },
20568                 {
20569                     cls : "", 
20570                     cn: [
20571                         inputblock
20572                     ]
20573                 }
20574             ];
20575             
20576             if(this.labelWidth > 12){
20577                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20578             }
20579             
20580             if(this.labelWidth < 13 && this.labelmd == 0){
20581                 this.labelmd = this.labelWidth;
20582             }
20583             
20584             if(this.labellg > 0){
20585                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20586                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20587             }
20588             
20589             if(this.labelmd > 0){
20590                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20591                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20592             }
20593             
20594             if(this.labelsm > 0){
20595                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20596                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20597             }
20598             
20599             if(this.labelxs > 0){
20600                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20601                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20602             }
20603             
20604         } else if ( this.fieldLabel.length) {
20605 //                Roo.log(" label");
20606                 cfg.cn = [
20607                    
20608                     {
20609                         tag: this.boxLabel ? 'span' : 'label',
20610                         'for': id,
20611                         cls: 'control-label box-input-label',
20612                         //cls : 'input-group-addon',
20613                         html : this.fieldLabel
20614                     },
20615                     
20616                     inputblock
20617                     
20618                 ];
20619
20620         } else {
20621             
20622 //                Roo.log(" no label && no align");
20623                 cfg.cn = [  inputblock ] ;
20624                 
20625                 
20626         }
20627         
20628         if(this.boxLabel){
20629              var boxLabelCfg = {
20630                 tag: 'label',
20631                 //'for': id, // box label is handled by onclick - so no for...
20632                 cls: 'box-label',
20633                 html: this.boxLabel
20634             };
20635             
20636             if(this.tooltip){
20637                 boxLabelCfg.tooltip = this.tooltip;
20638             }
20639              
20640             cfg.cn.push(boxLabelCfg);
20641         }
20642         
20643         if(this.inputType != 'radio'){
20644             cfg.cn.push(hidden);
20645         }
20646         
20647         return cfg;
20648         
20649     },
20650     
20651     /**
20652      * return the real input element.
20653      */
20654     inputEl: function ()
20655     {
20656         return this.el.select('input.roo-' + this.inputType,true).first();
20657     },
20658     hiddenEl: function ()
20659     {
20660         return this.el.select('input.roo-hidden-value',true).first();
20661     },
20662     
20663     labelEl: function()
20664     {
20665         return this.el.select('label.control-label',true).first();
20666     },
20667     /* depricated... */
20668     
20669     label: function()
20670     {
20671         return this.labelEl();
20672     },
20673     
20674     boxLabelEl: function()
20675     {
20676         return this.el.select('label.box-label',true).first();
20677     },
20678     
20679     initEvents : function()
20680     {
20681 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20682         
20683         this.inputEl().on('click', this.onClick,  this);
20684         
20685         if (this.boxLabel) { 
20686             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20687         }
20688         
20689         this.startValue = this.getValue();
20690         
20691         if(this.groupId){
20692             Roo.bootstrap.CheckBox.register(this);
20693         }
20694     },
20695     
20696     onClick : function(e)
20697     {   
20698         if(this.fireEvent('click', this, e) !== false){
20699             this.setChecked(!this.checked);
20700         }
20701         
20702     },
20703     
20704     setChecked : function(state,suppressEvent)
20705     {
20706         this.startValue = this.getValue();
20707
20708         if(this.inputType == 'radio'){
20709             
20710             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20711                 e.dom.checked = false;
20712             });
20713             
20714             this.inputEl().dom.checked = true;
20715             
20716             this.inputEl().dom.value = this.inputValue;
20717             
20718             if(suppressEvent !== true){
20719                 this.fireEvent('check', this, true);
20720             }
20721             
20722             this.validate();
20723             
20724             return;
20725         }
20726         
20727         this.checked = state;
20728         
20729         this.inputEl().dom.checked = state;
20730         
20731         
20732         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20733         
20734         if(suppressEvent !== true){
20735             this.fireEvent('check', this, state);
20736         }
20737         
20738         this.validate();
20739     },
20740     
20741     getValue : function()
20742     {
20743         if(this.inputType == 'radio'){
20744             return this.getGroupValue();
20745         }
20746         
20747         return this.hiddenEl().dom.value;
20748         
20749     },
20750     
20751     getGroupValue : function()
20752     {
20753         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20754             return '';
20755         }
20756         
20757         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20758     },
20759     
20760     setValue : function(v,suppressEvent)
20761     {
20762         if(this.inputType == 'radio'){
20763             this.setGroupValue(v, suppressEvent);
20764             return;
20765         }
20766         
20767         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20768         
20769         this.validate();
20770     },
20771     
20772     setGroupValue : function(v, suppressEvent)
20773     {
20774         this.startValue = this.getValue();
20775         
20776         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20777             e.dom.checked = false;
20778             
20779             if(e.dom.value == v){
20780                 e.dom.checked = true;
20781             }
20782         });
20783         
20784         if(suppressEvent !== true){
20785             this.fireEvent('check', this, true);
20786         }
20787
20788         this.validate();
20789         
20790         return;
20791     },
20792     
20793     validate : function()
20794     {
20795         if(this.getVisibilityEl().hasClass('hidden')){
20796             return true;
20797         }
20798         
20799         if(
20800                 this.disabled || 
20801                 (this.inputType == 'radio' && this.validateRadio()) ||
20802                 (this.inputType == 'checkbox' && this.validateCheckbox())
20803         ){
20804             this.markValid();
20805             return true;
20806         }
20807         
20808         this.markInvalid();
20809         return false;
20810     },
20811     
20812     validateRadio : function()
20813     {
20814         if(this.getVisibilityEl().hasClass('hidden')){
20815             return true;
20816         }
20817         
20818         if(this.allowBlank){
20819             return true;
20820         }
20821         
20822         var valid = false;
20823         
20824         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20825             if(!e.dom.checked){
20826                 return;
20827             }
20828             
20829             valid = true;
20830             
20831             return false;
20832         });
20833         
20834         return valid;
20835     },
20836     
20837     validateCheckbox : function()
20838     {
20839         if(!this.groupId){
20840             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20841             //return (this.getValue() == this.inputValue) ? true : false;
20842         }
20843         
20844         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20845         
20846         if(!group){
20847             return false;
20848         }
20849         
20850         var r = false;
20851         
20852         for(var i in group){
20853             if(group[i].el.isVisible(true)){
20854                 r = false;
20855                 break;
20856             }
20857             
20858             r = true;
20859         }
20860         
20861         for(var i in group){
20862             if(r){
20863                 break;
20864             }
20865             
20866             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20867         }
20868         
20869         return r;
20870     },
20871     
20872     /**
20873      * Mark this field as valid
20874      */
20875     markValid : function()
20876     {
20877         var _this = this;
20878         
20879         this.fireEvent('valid', this);
20880         
20881         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20882         
20883         if(this.groupId){
20884             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20885         }
20886         
20887         if(label){
20888             label.markValid();
20889         }
20890
20891         if(this.inputType == 'radio'){
20892             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20893                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20894                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20895             });
20896             
20897             return;
20898         }
20899
20900         if(!this.groupId){
20901             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20902             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20903             return;
20904         }
20905         
20906         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20907         
20908         if(!group){
20909             return;
20910         }
20911         
20912         for(var i in group){
20913             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20914             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20915         }
20916     },
20917     
20918      /**
20919      * Mark this field as invalid
20920      * @param {String} msg The validation message
20921      */
20922     markInvalid : function(msg)
20923     {
20924         if(this.allowBlank){
20925             return;
20926         }
20927         
20928         var _this = this;
20929         
20930         this.fireEvent('invalid', this, msg);
20931         
20932         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20933         
20934         if(this.groupId){
20935             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20936         }
20937         
20938         if(label){
20939             label.markInvalid();
20940         }
20941             
20942         if(this.inputType == 'radio'){
20943             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20945                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20946             });
20947             
20948             return;
20949         }
20950         
20951         if(!this.groupId){
20952             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20953             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20954             return;
20955         }
20956         
20957         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20958         
20959         if(!group){
20960             return;
20961         }
20962         
20963         for(var i in group){
20964             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20965             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20966         }
20967         
20968     },
20969     
20970     clearInvalid : function()
20971     {
20972         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20973         
20974         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20975         
20976         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20977         
20978         if (label && label.iconEl) {
20979             label.iconEl.removeClass(label.validClass);
20980             label.iconEl.removeClass(label.invalidClass);
20981         }
20982     },
20983     
20984     disable : function()
20985     {
20986         if(this.inputType != 'radio'){
20987             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20988             return;
20989         }
20990         
20991         var _this = this;
20992         
20993         if(this.rendered){
20994             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20995                 _this.getActionEl().addClass(this.disabledClass);
20996                 e.dom.disabled = true;
20997             });
20998         }
20999         
21000         this.disabled = true;
21001         this.fireEvent("disable", this);
21002         return this;
21003     },
21004
21005     enable : function()
21006     {
21007         if(this.inputType != 'radio'){
21008             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21009             return;
21010         }
21011         
21012         var _this = this;
21013         
21014         if(this.rendered){
21015             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21016                 _this.getActionEl().removeClass(this.disabledClass);
21017                 e.dom.disabled = false;
21018             });
21019         }
21020         
21021         this.disabled = false;
21022         this.fireEvent("enable", this);
21023         return this;
21024     },
21025     
21026     setBoxLabel : function(v)
21027     {
21028         this.boxLabel = v;
21029         
21030         if(this.rendered){
21031             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21032         }
21033     }
21034
21035 });
21036
21037 Roo.apply(Roo.bootstrap.CheckBox, {
21038     
21039     groups: {},
21040     
21041      /**
21042     * register a CheckBox Group
21043     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21044     */
21045     register : function(checkbox)
21046     {
21047         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21048             this.groups[checkbox.groupId] = {};
21049         }
21050         
21051         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21052             return;
21053         }
21054         
21055         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21056         
21057     },
21058     /**
21059     * fetch a CheckBox Group based on the group ID
21060     * @param {string} the group ID
21061     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21062     */
21063     get: function(groupId) {
21064         if (typeof(this.groups[groupId]) == 'undefined') {
21065             return false;
21066         }
21067         
21068         return this.groups[groupId] ;
21069     }
21070     
21071     
21072 });
21073 /*
21074  * - LGPL
21075  *
21076  * RadioItem
21077  * 
21078  */
21079
21080 /**
21081  * @class Roo.bootstrap.Radio
21082  * @extends Roo.bootstrap.Component
21083  * Bootstrap Radio class
21084  * @cfg {String} boxLabel - the label associated
21085  * @cfg {String} value - the value of radio
21086  * 
21087  * @constructor
21088  * Create a new Radio
21089  * @param {Object} config The config object
21090  */
21091 Roo.bootstrap.Radio = function(config){
21092     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21093     
21094 };
21095
21096 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21097     
21098     boxLabel : '',
21099     
21100     value : '',
21101     
21102     getAutoCreate : function()
21103     {
21104         var cfg = {
21105             tag : 'div',
21106             cls : 'form-group radio',
21107             cn : [
21108                 {
21109                     tag : 'label',
21110                     cls : 'box-label',
21111                     html : this.boxLabel
21112                 }
21113             ]
21114         };
21115         
21116         return cfg;
21117     },
21118     
21119     initEvents : function() 
21120     {
21121         this.parent().register(this);
21122         
21123         this.el.on('click', this.onClick, this);
21124         
21125     },
21126     
21127     onClick : function(e)
21128     {
21129         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21130             this.setChecked(true);
21131         }
21132     },
21133     
21134     setChecked : function(state, suppressEvent)
21135     {
21136         this.parent().setValue(this.value, suppressEvent);
21137         
21138     },
21139     
21140     setBoxLabel : function(v)
21141     {
21142         this.boxLabel = v;
21143         
21144         if(this.rendered){
21145             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21146         }
21147     }
21148     
21149 });
21150  
21151
21152  /*
21153  * - LGPL
21154  *
21155  * Input
21156  * 
21157  */
21158
21159 /**
21160  * @class Roo.bootstrap.SecurePass
21161  * @extends Roo.bootstrap.Input
21162  * Bootstrap SecurePass class
21163  *
21164  * 
21165  * @constructor
21166  * Create a new SecurePass
21167  * @param {Object} config The config object
21168  */
21169  
21170 Roo.bootstrap.SecurePass = function (config) {
21171     // these go here, so the translation tool can replace them..
21172     this.errors = {
21173         PwdEmpty: "Please type a password, and then retype it to confirm.",
21174         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21175         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21176         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21177         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21178         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21179         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21180         TooWeak: "Your password is Too Weak."
21181     },
21182     this.meterLabel = "Password strength:";
21183     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21184     this.meterClass = [
21185         "roo-password-meter-tooweak", 
21186         "roo-password-meter-weak", 
21187         "roo-password-meter-medium", 
21188         "roo-password-meter-strong", 
21189         "roo-password-meter-grey"
21190     ];
21191     
21192     this.errors = {};
21193     
21194     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21195 }
21196
21197 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21198     /**
21199      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21200      * {
21201      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21202      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21203      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21204      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21205      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21206      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21207      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21208      * })
21209      */
21210     // private
21211     
21212     meterWidth: 300,
21213     errorMsg :'',    
21214     errors: false,
21215     imageRoot: '/',
21216     /**
21217      * @cfg {String/Object} Label for the strength meter (defaults to
21218      * 'Password strength:')
21219      */
21220     // private
21221     meterLabel: '',
21222     /**
21223      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21224      * ['Weak', 'Medium', 'Strong'])
21225      */
21226     // private    
21227     pwdStrengths: false,    
21228     // private
21229     strength: 0,
21230     // private
21231     _lastPwd: null,
21232     // private
21233     kCapitalLetter: 0,
21234     kSmallLetter: 1,
21235     kDigit: 2,
21236     kPunctuation: 3,
21237     
21238     insecure: false,
21239     // private
21240     initEvents: function ()
21241     {
21242         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21243
21244         if (this.el.is('input[type=password]') && Roo.isSafari) {
21245             this.el.on('keydown', this.SafariOnKeyDown, this);
21246         }
21247
21248         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21249     },
21250     // private
21251     onRender: function (ct, position)
21252     {
21253         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21254         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21255         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21256
21257         this.trigger.createChild({
21258                    cn: [
21259                     {
21260                     //id: 'PwdMeter',
21261                     tag: 'div',
21262                     cls: 'roo-password-meter-grey col-xs-12',
21263                     style: {
21264                         //width: 0,
21265                         //width: this.meterWidth + 'px'                                                
21266                         }
21267                     },
21268                     {                            
21269                          cls: 'roo-password-meter-text'                          
21270                     }
21271                 ]            
21272         });
21273
21274          
21275         if (this.hideTrigger) {
21276             this.trigger.setDisplayed(false);
21277         }
21278         this.setSize(this.width || '', this.height || '');
21279     },
21280     // private
21281     onDestroy: function ()
21282     {
21283         if (this.trigger) {
21284             this.trigger.removeAllListeners();
21285             this.trigger.remove();
21286         }
21287         if (this.wrap) {
21288             this.wrap.remove();
21289         }
21290         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21291     },
21292     // private
21293     checkStrength: function ()
21294     {
21295         var pwd = this.inputEl().getValue();
21296         if (pwd == this._lastPwd) {
21297             return;
21298         }
21299
21300         var strength;
21301         if (this.ClientSideStrongPassword(pwd)) {
21302             strength = 3;
21303         } else if (this.ClientSideMediumPassword(pwd)) {
21304             strength = 2;
21305         } else if (this.ClientSideWeakPassword(pwd)) {
21306             strength = 1;
21307         } else {
21308             strength = 0;
21309         }
21310         
21311         Roo.log('strength1: ' + strength);
21312         
21313         //var pm = this.trigger.child('div/div/div').dom;
21314         var pm = this.trigger.child('div/div');
21315         pm.removeClass(this.meterClass);
21316         pm.addClass(this.meterClass[strength]);
21317                 
21318         
21319         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21320                 
21321         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21322         
21323         this._lastPwd = pwd;
21324     },
21325     reset: function ()
21326     {
21327         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21328         
21329         this._lastPwd = '';
21330         
21331         var pm = this.trigger.child('div/div');
21332         pm.removeClass(this.meterClass);
21333         pm.addClass('roo-password-meter-grey');        
21334         
21335         
21336         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21337         
21338         pt.innerHTML = '';
21339         this.inputEl().dom.type='password';
21340     },
21341     // private
21342     validateValue: function (value)
21343     {
21344         
21345         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21346             return false;
21347         }
21348         if (value.length == 0) {
21349             if (this.allowBlank) {
21350                 this.clearInvalid();
21351                 return true;
21352             }
21353
21354             this.markInvalid(this.errors.PwdEmpty);
21355             this.errorMsg = this.errors.PwdEmpty;
21356             return false;
21357         }
21358         
21359         if(this.insecure){
21360             return true;
21361         }
21362         
21363         if ('[\x21-\x7e]*'.match(value)) {
21364             this.markInvalid(this.errors.PwdBadChar);
21365             this.errorMsg = this.errors.PwdBadChar;
21366             return false;
21367         }
21368         if (value.length < 6) {
21369             this.markInvalid(this.errors.PwdShort);
21370             this.errorMsg = this.errors.PwdShort;
21371             return false;
21372         }
21373         if (value.length > 16) {
21374             this.markInvalid(this.errors.PwdLong);
21375             this.errorMsg = this.errors.PwdLong;
21376             return false;
21377         }
21378         var strength;
21379         if (this.ClientSideStrongPassword(value)) {
21380             strength = 3;
21381         } else if (this.ClientSideMediumPassword(value)) {
21382             strength = 2;
21383         } else if (this.ClientSideWeakPassword(value)) {
21384             strength = 1;
21385         } else {
21386             strength = 0;
21387         }
21388
21389         
21390         if (strength < 2) {
21391             //this.markInvalid(this.errors.TooWeak);
21392             this.errorMsg = this.errors.TooWeak;
21393             //return false;
21394         }
21395         
21396         
21397         console.log('strength2: ' + strength);
21398         
21399         //var pm = this.trigger.child('div/div/div').dom;
21400         
21401         var pm = this.trigger.child('div/div');
21402         pm.removeClass(this.meterClass);
21403         pm.addClass(this.meterClass[strength]);
21404                 
21405         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21406                 
21407         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21408         
21409         this.errorMsg = ''; 
21410         return true;
21411     },
21412     // private
21413     CharacterSetChecks: function (type)
21414     {
21415         this.type = type;
21416         this.fResult = false;
21417     },
21418     // private
21419     isctype: function (character, type)
21420     {
21421         switch (type) {  
21422             case this.kCapitalLetter:
21423                 if (character >= 'A' && character <= 'Z') {
21424                     return true;
21425                 }
21426                 break;
21427             
21428             case this.kSmallLetter:
21429                 if (character >= 'a' && character <= 'z') {
21430                     return true;
21431                 }
21432                 break;
21433             
21434             case this.kDigit:
21435                 if (character >= '0' && character <= '9') {
21436                     return true;
21437                 }
21438                 break;
21439             
21440             case this.kPunctuation:
21441                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21442                     return true;
21443                 }
21444                 break;
21445             
21446             default:
21447                 return false;
21448         }
21449
21450     },
21451     // private
21452     IsLongEnough: function (pwd, size)
21453     {
21454         return !(pwd == null || isNaN(size) || pwd.length < size);
21455     },
21456     // private
21457     SpansEnoughCharacterSets: function (word, nb)
21458     {
21459         if (!this.IsLongEnough(word, nb))
21460         {
21461             return false;
21462         }
21463
21464         var characterSetChecks = new Array(
21465             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21466             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21467         );
21468         
21469         for (var index = 0; index < word.length; ++index) {
21470             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21471                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21472                     characterSetChecks[nCharSet].fResult = true;
21473                     break;
21474                 }
21475             }
21476         }
21477
21478         var nCharSets = 0;
21479         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21480             if (characterSetChecks[nCharSet].fResult) {
21481                 ++nCharSets;
21482             }
21483         }
21484
21485         if (nCharSets < nb) {
21486             return false;
21487         }
21488         return true;
21489     },
21490     // private
21491     ClientSideStrongPassword: function (pwd)
21492     {
21493         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21494     },
21495     // private
21496     ClientSideMediumPassword: function (pwd)
21497     {
21498         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21499     },
21500     // private
21501     ClientSideWeakPassword: function (pwd)
21502     {
21503         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21504     }
21505           
21506 })//<script type="text/javascript">
21507
21508 /*
21509  * Based  Ext JS Library 1.1.1
21510  * Copyright(c) 2006-2007, Ext JS, LLC.
21511  * LGPL
21512  *
21513  */
21514  
21515 /**
21516  * @class Roo.HtmlEditorCore
21517  * @extends Roo.Component
21518  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21519  *
21520  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21521  */
21522
21523 Roo.HtmlEditorCore = function(config){
21524     
21525     
21526     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21527     
21528     
21529     this.addEvents({
21530         /**
21531          * @event initialize
21532          * Fires when the editor is fully initialized (including the iframe)
21533          * @param {Roo.HtmlEditorCore} this
21534          */
21535         initialize: true,
21536         /**
21537          * @event activate
21538          * Fires when the editor is first receives the focus. Any insertion must wait
21539          * until after this event.
21540          * @param {Roo.HtmlEditorCore} this
21541          */
21542         activate: true,
21543          /**
21544          * @event beforesync
21545          * Fires before the textarea is updated with content from the editor iframe. Return false
21546          * to cancel the sync.
21547          * @param {Roo.HtmlEditorCore} this
21548          * @param {String} html
21549          */
21550         beforesync: true,
21551          /**
21552          * @event beforepush
21553          * Fires before the iframe editor is updated with content from the textarea. Return false
21554          * to cancel the push.
21555          * @param {Roo.HtmlEditorCore} this
21556          * @param {String} html
21557          */
21558         beforepush: true,
21559          /**
21560          * @event sync
21561          * Fires when the textarea is updated with content from the editor iframe.
21562          * @param {Roo.HtmlEditorCore} this
21563          * @param {String} html
21564          */
21565         sync: true,
21566          /**
21567          * @event push
21568          * Fires when the iframe editor is updated with content from the textarea.
21569          * @param {Roo.HtmlEditorCore} this
21570          * @param {String} html
21571          */
21572         push: true,
21573         
21574         /**
21575          * @event editorevent
21576          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21577          * @param {Roo.HtmlEditorCore} this
21578          */
21579         editorevent: true
21580         
21581     });
21582     
21583     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21584     
21585     // defaults : white / black...
21586     this.applyBlacklists();
21587     
21588     
21589     
21590 };
21591
21592
21593 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21594
21595
21596      /**
21597      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21598      */
21599     
21600     owner : false,
21601     
21602      /**
21603      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21604      *                        Roo.resizable.
21605      */
21606     resizable : false,
21607      /**
21608      * @cfg {Number} height (in pixels)
21609      */   
21610     height: 300,
21611    /**
21612      * @cfg {Number} width (in pixels)
21613      */   
21614     width: 500,
21615     
21616     /**
21617      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21618      * 
21619      */
21620     stylesheets: false,
21621     
21622     // id of frame..
21623     frameId: false,
21624     
21625     // private properties
21626     validationEvent : false,
21627     deferHeight: true,
21628     initialized : false,
21629     activated : false,
21630     sourceEditMode : false,
21631     onFocus : Roo.emptyFn,
21632     iframePad:3,
21633     hideMode:'offsets',
21634     
21635     clearUp: true,
21636     
21637     // blacklist + whitelisted elements..
21638     black: false,
21639     white: false,
21640      
21641     bodyCls : '',
21642
21643     /**
21644      * Protected method that will not generally be called directly. It
21645      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21646      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21647      */
21648     getDocMarkup : function(){
21649         // body styles..
21650         var st = '';
21651         
21652         // inherit styels from page...?? 
21653         if (this.stylesheets === false) {
21654             
21655             Roo.get(document.head).select('style').each(function(node) {
21656                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21657             });
21658             
21659             Roo.get(document.head).select('link').each(function(node) { 
21660                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21661             });
21662             
21663         } else if (!this.stylesheets.length) {
21664                 // simple..
21665                 st = '<style type="text/css">' +
21666                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21667                    '</style>';
21668         } else { 
21669             st = '<style type="text/css">' +
21670                     this.stylesheets +
21671                 '</style>';
21672         }
21673         
21674         st +=  '<style type="text/css">' +
21675             'IMG { cursor: pointer } ' +
21676         '</style>';
21677
21678         var cls = 'roo-htmleditor-body';
21679         
21680         if(this.bodyCls.length){
21681             cls += ' ' + this.bodyCls;
21682         }
21683         
21684         return '<html><head>' + st  +
21685             //<style type="text/css">' +
21686             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21687             //'</style>' +
21688             ' </head><body class="' +  cls + '"></body></html>';
21689     },
21690
21691     // private
21692     onRender : function(ct, position)
21693     {
21694         var _t = this;
21695         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21696         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21697         
21698         
21699         this.el.dom.style.border = '0 none';
21700         this.el.dom.setAttribute('tabIndex', -1);
21701         this.el.addClass('x-hidden hide');
21702         
21703         
21704         
21705         if(Roo.isIE){ // fix IE 1px bogus margin
21706             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21707         }
21708        
21709         
21710         this.frameId = Roo.id();
21711         
21712          
21713         
21714         var iframe = this.owner.wrap.createChild({
21715             tag: 'iframe',
21716             cls: 'form-control', // bootstrap..
21717             id: this.frameId,
21718             name: this.frameId,
21719             frameBorder : 'no',
21720             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21721         }, this.el
21722         );
21723         
21724         
21725         this.iframe = iframe.dom;
21726
21727          this.assignDocWin();
21728         
21729         this.doc.designMode = 'on';
21730        
21731         this.doc.open();
21732         this.doc.write(this.getDocMarkup());
21733         this.doc.close();
21734
21735         
21736         var task = { // must defer to wait for browser to be ready
21737             run : function(){
21738                 //console.log("run task?" + this.doc.readyState);
21739                 this.assignDocWin();
21740                 if(this.doc.body || this.doc.readyState == 'complete'){
21741                     try {
21742                         this.doc.designMode="on";
21743                     } catch (e) {
21744                         return;
21745                     }
21746                     Roo.TaskMgr.stop(task);
21747                     this.initEditor.defer(10, this);
21748                 }
21749             },
21750             interval : 10,
21751             duration: 10000,
21752             scope: this
21753         };
21754         Roo.TaskMgr.start(task);
21755
21756     },
21757
21758     // private
21759     onResize : function(w, h)
21760     {
21761          Roo.log('resize: ' +w + ',' + h );
21762         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21763         if(!this.iframe){
21764             return;
21765         }
21766         if(typeof w == 'number'){
21767             
21768             this.iframe.style.width = w + 'px';
21769         }
21770         if(typeof h == 'number'){
21771             
21772             this.iframe.style.height = h + 'px';
21773             if(this.doc){
21774                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21775             }
21776         }
21777         
21778     },
21779
21780     /**
21781      * Toggles the editor between standard and source edit mode.
21782      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21783      */
21784     toggleSourceEdit : function(sourceEditMode){
21785         
21786         this.sourceEditMode = sourceEditMode === true;
21787         
21788         if(this.sourceEditMode){
21789  
21790             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21791             
21792         }else{
21793             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21794             //this.iframe.className = '';
21795             this.deferFocus();
21796         }
21797         //this.setSize(this.owner.wrap.getSize());
21798         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21799     },
21800
21801     
21802   
21803
21804     /**
21805      * Protected method that will not generally be called directly. If you need/want
21806      * custom HTML cleanup, this is the method you should override.
21807      * @param {String} html The HTML to be cleaned
21808      * return {String} The cleaned HTML
21809      */
21810     cleanHtml : function(html){
21811         html = String(html);
21812         if(html.length > 5){
21813             if(Roo.isSafari){ // strip safari nonsense
21814                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21815             }
21816         }
21817         if(html == '&nbsp;'){
21818             html = '';
21819         }
21820         return html;
21821     },
21822
21823     /**
21824      * HTML Editor -> Textarea
21825      * Protected method that will not generally be called directly. Syncs the contents
21826      * of the editor iframe with the textarea.
21827      */
21828     syncValue : function(){
21829         if(this.initialized){
21830             var bd = (this.doc.body || this.doc.documentElement);
21831             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21832             var html = bd.innerHTML;
21833             if(Roo.isSafari){
21834                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21835                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21836                 if(m && m[1]){
21837                     html = '<div style="'+m[0]+'">' + html + '</div>';
21838                 }
21839             }
21840             html = this.cleanHtml(html);
21841             // fix up the special chars.. normaly like back quotes in word...
21842             // however we do not want to do this with chinese..
21843             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21844                 var cc = b.charCodeAt();
21845                 if (
21846                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21847                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21848                     (cc >= 0xf900 && cc < 0xfb00 )
21849                 ) {
21850                         return b;
21851                 }
21852                 return "&#"+cc+";" 
21853             });
21854             if(this.owner.fireEvent('beforesync', this, html) !== false){
21855                 this.el.dom.value = html;
21856                 this.owner.fireEvent('sync', this, html);
21857             }
21858         }
21859     },
21860
21861     /**
21862      * Protected method that will not generally be called directly. Pushes the value of the textarea
21863      * into the iframe editor.
21864      */
21865     pushValue : function(){
21866         if(this.initialized){
21867             var v = this.el.dom.value.trim();
21868             
21869 //            if(v.length < 1){
21870 //                v = '&#160;';
21871 //            }
21872             
21873             if(this.owner.fireEvent('beforepush', this, v) !== false){
21874                 var d = (this.doc.body || this.doc.documentElement);
21875                 d.innerHTML = v;
21876                 this.cleanUpPaste();
21877                 this.el.dom.value = d.innerHTML;
21878                 this.owner.fireEvent('push', this, v);
21879             }
21880         }
21881     },
21882
21883     // private
21884     deferFocus : function(){
21885         this.focus.defer(10, this);
21886     },
21887
21888     // doc'ed in Field
21889     focus : function(){
21890         if(this.win && !this.sourceEditMode){
21891             this.win.focus();
21892         }else{
21893             this.el.focus();
21894         }
21895     },
21896     
21897     assignDocWin: function()
21898     {
21899         var iframe = this.iframe;
21900         
21901          if(Roo.isIE){
21902             this.doc = iframe.contentWindow.document;
21903             this.win = iframe.contentWindow;
21904         } else {
21905 //            if (!Roo.get(this.frameId)) {
21906 //                return;
21907 //            }
21908 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21909 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21910             
21911             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21912                 return;
21913             }
21914             
21915             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21916             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21917         }
21918     },
21919     
21920     // private
21921     initEditor : function(){
21922         //console.log("INIT EDITOR");
21923         this.assignDocWin();
21924         
21925         
21926         
21927         this.doc.designMode="on";
21928         this.doc.open();
21929         this.doc.write(this.getDocMarkup());
21930         this.doc.close();
21931         
21932         var dbody = (this.doc.body || this.doc.documentElement);
21933         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21934         // this copies styles from the containing element into thsi one..
21935         // not sure why we need all of this..
21936         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21937         
21938         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21939         //ss['background-attachment'] = 'fixed'; // w3c
21940         dbody.bgProperties = 'fixed'; // ie
21941         //Roo.DomHelper.applyStyles(dbody, ss);
21942         Roo.EventManager.on(this.doc, {
21943             //'mousedown': this.onEditorEvent,
21944             'mouseup': this.onEditorEvent,
21945             'dblclick': this.onEditorEvent,
21946             'click': this.onEditorEvent,
21947             'keyup': this.onEditorEvent,
21948             buffer:100,
21949             scope: this
21950         });
21951         if(Roo.isGecko){
21952             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21953         }
21954         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21955             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21956         }
21957         this.initialized = true;
21958
21959         this.owner.fireEvent('initialize', this);
21960         this.pushValue();
21961     },
21962
21963     // private
21964     onDestroy : function(){
21965         
21966         
21967         
21968         if(this.rendered){
21969             
21970             //for (var i =0; i < this.toolbars.length;i++) {
21971             //    // fixme - ask toolbars for heights?
21972             //    this.toolbars[i].onDestroy();
21973            // }
21974             
21975             //this.wrap.dom.innerHTML = '';
21976             //this.wrap.remove();
21977         }
21978     },
21979
21980     // private
21981     onFirstFocus : function(){
21982         
21983         this.assignDocWin();
21984         
21985         
21986         this.activated = true;
21987          
21988     
21989         if(Roo.isGecko){ // prevent silly gecko errors
21990             this.win.focus();
21991             var s = this.win.getSelection();
21992             if(!s.focusNode || s.focusNode.nodeType != 3){
21993                 var r = s.getRangeAt(0);
21994                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21995                 r.collapse(true);
21996                 this.deferFocus();
21997             }
21998             try{
21999                 this.execCmd('useCSS', true);
22000                 this.execCmd('styleWithCSS', false);
22001             }catch(e){}
22002         }
22003         this.owner.fireEvent('activate', this);
22004     },
22005
22006     // private
22007     adjustFont: function(btn){
22008         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22009         //if(Roo.isSafari){ // safari
22010         //    adjust *= 2;
22011        // }
22012         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22013         if(Roo.isSafari){ // safari
22014             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22015             v =  (v < 10) ? 10 : v;
22016             v =  (v > 48) ? 48 : v;
22017             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22018             
22019         }
22020         
22021         
22022         v = Math.max(1, v+adjust);
22023         
22024         this.execCmd('FontSize', v  );
22025     },
22026
22027     onEditorEvent : function(e)
22028     {
22029         this.owner.fireEvent('editorevent', this, e);
22030       //  this.updateToolbar();
22031         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22032     },
22033
22034     insertTag : function(tg)
22035     {
22036         // could be a bit smarter... -> wrap the current selected tRoo..
22037         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22038             
22039             range = this.createRange(this.getSelection());
22040             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22041             wrappingNode.appendChild(range.extractContents());
22042             range.insertNode(wrappingNode);
22043
22044             return;
22045             
22046             
22047             
22048         }
22049         this.execCmd("formatblock",   tg);
22050         
22051     },
22052     
22053     insertText : function(txt)
22054     {
22055         
22056         
22057         var range = this.createRange();
22058         range.deleteContents();
22059                //alert(Sender.getAttribute('label'));
22060                
22061         range.insertNode(this.doc.createTextNode(txt));
22062     } ,
22063     
22064      
22065
22066     /**
22067      * Executes a Midas editor command on the editor document and performs necessary focus and
22068      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22069      * @param {String} cmd The Midas command
22070      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22071      */
22072     relayCmd : function(cmd, value){
22073         this.win.focus();
22074         this.execCmd(cmd, value);
22075         this.owner.fireEvent('editorevent', this);
22076         //this.updateToolbar();
22077         this.owner.deferFocus();
22078     },
22079
22080     /**
22081      * Executes a Midas editor command directly on the editor document.
22082      * For visual commands, you should use {@link #relayCmd} instead.
22083      * <b>This should only be called after the editor is initialized.</b>
22084      * @param {String} cmd The Midas command
22085      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22086      */
22087     execCmd : function(cmd, value){
22088         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22089         this.syncValue();
22090     },
22091  
22092  
22093    
22094     /**
22095      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22096      * to insert tRoo.
22097      * @param {String} text | dom node.. 
22098      */
22099     insertAtCursor : function(text)
22100     {
22101         
22102         if(!this.activated){
22103             return;
22104         }
22105         /*
22106         if(Roo.isIE){
22107             this.win.focus();
22108             var r = this.doc.selection.createRange();
22109             if(r){
22110                 r.collapse(true);
22111                 r.pasteHTML(text);
22112                 this.syncValue();
22113                 this.deferFocus();
22114             
22115             }
22116             return;
22117         }
22118         */
22119         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22120             this.win.focus();
22121             
22122             
22123             // from jquery ui (MIT licenced)
22124             var range, node;
22125             var win = this.win;
22126             
22127             if (win.getSelection && win.getSelection().getRangeAt) {
22128                 range = win.getSelection().getRangeAt(0);
22129                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22130                 range.insertNode(node);
22131             } else if (win.document.selection && win.document.selection.createRange) {
22132                 // no firefox support
22133                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22134                 win.document.selection.createRange().pasteHTML(txt);
22135             } else {
22136                 // no firefox support
22137                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22138                 this.execCmd('InsertHTML', txt);
22139             } 
22140             
22141             this.syncValue();
22142             
22143             this.deferFocus();
22144         }
22145     },
22146  // private
22147     mozKeyPress : function(e){
22148         if(e.ctrlKey){
22149             var c = e.getCharCode(), cmd;
22150           
22151             if(c > 0){
22152                 c = String.fromCharCode(c).toLowerCase();
22153                 switch(c){
22154                     case 'b':
22155                         cmd = 'bold';
22156                         break;
22157                     case 'i':
22158                         cmd = 'italic';
22159                         break;
22160                     
22161                     case 'u':
22162                         cmd = 'underline';
22163                         break;
22164                     
22165                     case 'v':
22166                         this.cleanUpPaste.defer(100, this);
22167                         return;
22168                         
22169                 }
22170                 if(cmd){
22171                     this.win.focus();
22172                     this.execCmd(cmd);
22173                     this.deferFocus();
22174                     e.preventDefault();
22175                 }
22176                 
22177             }
22178         }
22179     },
22180
22181     // private
22182     fixKeys : function(){ // load time branching for fastest keydown performance
22183         if(Roo.isIE){
22184             return function(e){
22185                 var k = e.getKey(), r;
22186                 if(k == e.TAB){
22187                     e.stopEvent();
22188                     r = this.doc.selection.createRange();
22189                     if(r){
22190                         r.collapse(true);
22191                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22192                         this.deferFocus();
22193                     }
22194                     return;
22195                 }
22196                 
22197                 if(k == e.ENTER){
22198                     r = this.doc.selection.createRange();
22199                     if(r){
22200                         var target = r.parentElement();
22201                         if(!target || target.tagName.toLowerCase() != 'li'){
22202                             e.stopEvent();
22203                             r.pasteHTML('<br />');
22204                             r.collapse(false);
22205                             r.select();
22206                         }
22207                     }
22208                 }
22209                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22210                     this.cleanUpPaste.defer(100, this);
22211                     return;
22212                 }
22213                 
22214                 
22215             };
22216         }else if(Roo.isOpera){
22217             return function(e){
22218                 var k = e.getKey();
22219                 if(k == e.TAB){
22220                     e.stopEvent();
22221                     this.win.focus();
22222                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22223                     this.deferFocus();
22224                 }
22225                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22226                     this.cleanUpPaste.defer(100, this);
22227                     return;
22228                 }
22229                 
22230             };
22231         }else if(Roo.isSafari){
22232             return function(e){
22233                 var k = e.getKey();
22234                 
22235                 if(k == e.TAB){
22236                     e.stopEvent();
22237                     this.execCmd('InsertText','\t');
22238                     this.deferFocus();
22239                     return;
22240                 }
22241                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22242                     this.cleanUpPaste.defer(100, this);
22243                     return;
22244                 }
22245                 
22246              };
22247         }
22248     }(),
22249     
22250     getAllAncestors: function()
22251     {
22252         var p = this.getSelectedNode();
22253         var a = [];
22254         if (!p) {
22255             a.push(p); // push blank onto stack..
22256             p = this.getParentElement();
22257         }
22258         
22259         
22260         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22261             a.push(p);
22262             p = p.parentNode;
22263         }
22264         a.push(this.doc.body);
22265         return a;
22266     },
22267     lastSel : false,
22268     lastSelNode : false,
22269     
22270     
22271     getSelection : function() 
22272     {
22273         this.assignDocWin();
22274         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22275     },
22276     
22277     getSelectedNode: function() 
22278     {
22279         // this may only work on Gecko!!!
22280         
22281         // should we cache this!!!!
22282         
22283         
22284         
22285          
22286         var range = this.createRange(this.getSelection()).cloneRange();
22287         
22288         if (Roo.isIE) {
22289             var parent = range.parentElement();
22290             while (true) {
22291                 var testRange = range.duplicate();
22292                 testRange.moveToElementText(parent);
22293                 if (testRange.inRange(range)) {
22294                     break;
22295                 }
22296                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22297                     break;
22298                 }
22299                 parent = parent.parentElement;
22300             }
22301             return parent;
22302         }
22303         
22304         // is ancestor a text element.
22305         var ac =  range.commonAncestorContainer;
22306         if (ac.nodeType == 3) {
22307             ac = ac.parentNode;
22308         }
22309         
22310         var ar = ac.childNodes;
22311          
22312         var nodes = [];
22313         var other_nodes = [];
22314         var has_other_nodes = false;
22315         for (var i=0;i<ar.length;i++) {
22316             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22317                 continue;
22318             }
22319             // fullly contained node.
22320             
22321             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22322                 nodes.push(ar[i]);
22323                 continue;
22324             }
22325             
22326             // probably selected..
22327             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22328                 other_nodes.push(ar[i]);
22329                 continue;
22330             }
22331             // outer..
22332             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22333                 continue;
22334             }
22335             
22336             
22337             has_other_nodes = true;
22338         }
22339         if (!nodes.length && other_nodes.length) {
22340             nodes= other_nodes;
22341         }
22342         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22343             return false;
22344         }
22345         
22346         return nodes[0];
22347     },
22348     createRange: function(sel)
22349     {
22350         // this has strange effects when using with 
22351         // top toolbar - not sure if it's a great idea.
22352         //this.editor.contentWindow.focus();
22353         if (typeof sel != "undefined") {
22354             try {
22355                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22356             } catch(e) {
22357                 return this.doc.createRange();
22358             }
22359         } else {
22360             return this.doc.createRange();
22361         }
22362     },
22363     getParentElement: function()
22364     {
22365         
22366         this.assignDocWin();
22367         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22368         
22369         var range = this.createRange(sel);
22370          
22371         try {
22372             var p = range.commonAncestorContainer;
22373             while (p.nodeType == 3) { // text node
22374                 p = p.parentNode;
22375             }
22376             return p;
22377         } catch (e) {
22378             return null;
22379         }
22380     
22381     },
22382     /***
22383      *
22384      * Range intersection.. the hard stuff...
22385      *  '-1' = before
22386      *  '0' = hits..
22387      *  '1' = after.
22388      *         [ -- selected range --- ]
22389      *   [fail]                        [fail]
22390      *
22391      *    basically..
22392      *      if end is before start or  hits it. fail.
22393      *      if start is after end or hits it fail.
22394      *
22395      *   if either hits (but other is outside. - then it's not 
22396      *   
22397      *    
22398      **/
22399     
22400     
22401     // @see http://www.thismuchiknow.co.uk/?p=64.
22402     rangeIntersectsNode : function(range, node)
22403     {
22404         var nodeRange = node.ownerDocument.createRange();
22405         try {
22406             nodeRange.selectNode(node);
22407         } catch (e) {
22408             nodeRange.selectNodeContents(node);
22409         }
22410     
22411         var rangeStartRange = range.cloneRange();
22412         rangeStartRange.collapse(true);
22413     
22414         var rangeEndRange = range.cloneRange();
22415         rangeEndRange.collapse(false);
22416     
22417         var nodeStartRange = nodeRange.cloneRange();
22418         nodeStartRange.collapse(true);
22419     
22420         var nodeEndRange = nodeRange.cloneRange();
22421         nodeEndRange.collapse(false);
22422     
22423         return rangeStartRange.compareBoundaryPoints(
22424                  Range.START_TO_START, nodeEndRange) == -1 &&
22425                rangeEndRange.compareBoundaryPoints(
22426                  Range.START_TO_START, nodeStartRange) == 1;
22427         
22428          
22429     },
22430     rangeCompareNode : function(range, node)
22431     {
22432         var nodeRange = node.ownerDocument.createRange();
22433         try {
22434             nodeRange.selectNode(node);
22435         } catch (e) {
22436             nodeRange.selectNodeContents(node);
22437         }
22438         
22439         
22440         range.collapse(true);
22441     
22442         nodeRange.collapse(true);
22443      
22444         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22445         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22446          
22447         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22448         
22449         var nodeIsBefore   =  ss == 1;
22450         var nodeIsAfter    = ee == -1;
22451         
22452         if (nodeIsBefore && nodeIsAfter) {
22453             return 0; // outer
22454         }
22455         if (!nodeIsBefore && nodeIsAfter) {
22456             return 1; //right trailed.
22457         }
22458         
22459         if (nodeIsBefore && !nodeIsAfter) {
22460             return 2;  // left trailed.
22461         }
22462         // fully contined.
22463         return 3;
22464     },
22465
22466     // private? - in a new class?
22467     cleanUpPaste :  function()
22468     {
22469         // cleans up the whole document..
22470         Roo.log('cleanuppaste');
22471         
22472         this.cleanUpChildren(this.doc.body);
22473         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22474         if (clean != this.doc.body.innerHTML) {
22475             this.doc.body.innerHTML = clean;
22476         }
22477         
22478     },
22479     
22480     cleanWordChars : function(input) {// change the chars to hex code
22481         var he = Roo.HtmlEditorCore;
22482         
22483         var output = input;
22484         Roo.each(he.swapCodes, function(sw) { 
22485             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22486             
22487             output = output.replace(swapper, sw[1]);
22488         });
22489         
22490         return output;
22491     },
22492     
22493     
22494     cleanUpChildren : function (n)
22495     {
22496         if (!n.childNodes.length) {
22497             return;
22498         }
22499         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22500            this.cleanUpChild(n.childNodes[i]);
22501         }
22502     },
22503     
22504     
22505         
22506     
22507     cleanUpChild : function (node)
22508     {
22509         var ed = this;
22510         //console.log(node);
22511         if (node.nodeName == "#text") {
22512             // clean up silly Windows -- stuff?
22513             return; 
22514         }
22515         if (node.nodeName == "#comment") {
22516             node.parentNode.removeChild(node);
22517             // clean up silly Windows -- stuff?
22518             return; 
22519         }
22520         var lcname = node.tagName.toLowerCase();
22521         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22522         // whitelist of tags..
22523         
22524         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22525             // remove node.
22526             node.parentNode.removeChild(node);
22527             return;
22528             
22529         }
22530         
22531         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22532         
22533         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22534         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22535         
22536         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22537         //    remove_keep_children = true;
22538         //}
22539         
22540         if (remove_keep_children) {
22541             this.cleanUpChildren(node);
22542             // inserts everything just before this node...
22543             while (node.childNodes.length) {
22544                 var cn = node.childNodes[0];
22545                 node.removeChild(cn);
22546                 node.parentNode.insertBefore(cn, node);
22547             }
22548             node.parentNode.removeChild(node);
22549             return;
22550         }
22551         
22552         if (!node.attributes || !node.attributes.length) {
22553             this.cleanUpChildren(node);
22554             return;
22555         }
22556         
22557         function cleanAttr(n,v)
22558         {
22559             
22560             if (v.match(/^\./) || v.match(/^\//)) {
22561                 return;
22562             }
22563             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22564                 return;
22565             }
22566             if (v.match(/^#/)) {
22567                 return;
22568             }
22569 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22570             node.removeAttribute(n);
22571             
22572         }
22573         
22574         var cwhite = this.cwhite;
22575         var cblack = this.cblack;
22576             
22577         function cleanStyle(n,v)
22578         {
22579             if (v.match(/expression/)) { //XSS?? should we even bother..
22580                 node.removeAttribute(n);
22581                 return;
22582             }
22583             
22584             var parts = v.split(/;/);
22585             var clean = [];
22586             
22587             Roo.each(parts, function(p) {
22588                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22589                 if (!p.length) {
22590                     return true;
22591                 }
22592                 var l = p.split(':').shift().replace(/\s+/g,'');
22593                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22594                 
22595                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22596 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22597                     //node.removeAttribute(n);
22598                     return true;
22599                 }
22600                 //Roo.log()
22601                 // only allow 'c whitelisted system attributes'
22602                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22603 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22604                     //node.removeAttribute(n);
22605                     return true;
22606                 }
22607                 
22608                 
22609                  
22610                 
22611                 clean.push(p);
22612                 return true;
22613             });
22614             if (clean.length) { 
22615                 node.setAttribute(n, clean.join(';'));
22616             } else {
22617                 node.removeAttribute(n);
22618             }
22619             
22620         }
22621         
22622         
22623         for (var i = node.attributes.length-1; i > -1 ; i--) {
22624             var a = node.attributes[i];
22625             //console.log(a);
22626             
22627             if (a.name.toLowerCase().substr(0,2)=='on')  {
22628                 node.removeAttribute(a.name);
22629                 continue;
22630             }
22631             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22632                 node.removeAttribute(a.name);
22633                 continue;
22634             }
22635             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22636                 cleanAttr(a.name,a.value); // fixme..
22637                 continue;
22638             }
22639             if (a.name == 'style') {
22640                 cleanStyle(a.name,a.value);
22641                 continue;
22642             }
22643             /// clean up MS crap..
22644             // tecnically this should be a list of valid class'es..
22645             
22646             
22647             if (a.name == 'class') {
22648                 if (a.value.match(/^Mso/)) {
22649                     node.className = '';
22650                 }
22651                 
22652                 if (a.value.match(/^body$/)) {
22653                     node.className = '';
22654                 }
22655                 continue;
22656             }
22657             
22658             // style cleanup!?
22659             // class cleanup?
22660             
22661         }
22662         
22663         
22664         this.cleanUpChildren(node);
22665         
22666         
22667     },
22668     
22669     /**
22670      * Clean up MS wordisms...
22671      */
22672     cleanWord : function(node)
22673     {
22674         
22675         
22676         if (!node) {
22677             this.cleanWord(this.doc.body);
22678             return;
22679         }
22680         if (node.nodeName == "#text") {
22681             // clean up silly Windows -- stuff?
22682             return; 
22683         }
22684         if (node.nodeName == "#comment") {
22685             node.parentNode.removeChild(node);
22686             // clean up silly Windows -- stuff?
22687             return; 
22688         }
22689         
22690         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22691             node.parentNode.removeChild(node);
22692             return;
22693         }
22694         
22695         // remove - but keep children..
22696         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22697             while (node.childNodes.length) {
22698                 var cn = node.childNodes[0];
22699                 node.removeChild(cn);
22700                 node.parentNode.insertBefore(cn, node);
22701             }
22702             node.parentNode.removeChild(node);
22703             this.iterateChildren(node, this.cleanWord);
22704             return;
22705         }
22706         // clean styles
22707         if (node.className.length) {
22708             
22709             var cn = node.className.split(/\W+/);
22710             var cna = [];
22711             Roo.each(cn, function(cls) {
22712                 if (cls.match(/Mso[a-zA-Z]+/)) {
22713                     return;
22714                 }
22715                 cna.push(cls);
22716             });
22717             node.className = cna.length ? cna.join(' ') : '';
22718             if (!cna.length) {
22719                 node.removeAttribute("class");
22720             }
22721         }
22722         
22723         if (node.hasAttribute("lang")) {
22724             node.removeAttribute("lang");
22725         }
22726         
22727         if (node.hasAttribute("style")) {
22728             
22729             var styles = node.getAttribute("style").split(";");
22730             var nstyle = [];
22731             Roo.each(styles, function(s) {
22732                 if (!s.match(/:/)) {
22733                     return;
22734                 }
22735                 var kv = s.split(":");
22736                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22737                     return;
22738                 }
22739                 // what ever is left... we allow.
22740                 nstyle.push(s);
22741             });
22742             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22743             if (!nstyle.length) {
22744                 node.removeAttribute('style');
22745             }
22746         }
22747         this.iterateChildren(node, this.cleanWord);
22748         
22749         
22750         
22751     },
22752     /**
22753      * iterateChildren of a Node, calling fn each time, using this as the scole..
22754      * @param {DomNode} node node to iterate children of.
22755      * @param {Function} fn method of this class to call on each item.
22756      */
22757     iterateChildren : function(node, fn)
22758     {
22759         if (!node.childNodes.length) {
22760                 return;
22761         }
22762         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22763            fn.call(this, node.childNodes[i])
22764         }
22765     },
22766     
22767     
22768     /**
22769      * cleanTableWidths.
22770      *
22771      * Quite often pasting from word etc.. results in tables with column and widths.
22772      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22773      *
22774      */
22775     cleanTableWidths : function(node)
22776     {
22777          
22778          
22779         if (!node) {
22780             this.cleanTableWidths(this.doc.body);
22781             return;
22782         }
22783         
22784         // ignore list...
22785         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22786             return; 
22787         }
22788         Roo.log(node.tagName);
22789         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22790             this.iterateChildren(node, this.cleanTableWidths);
22791             return;
22792         }
22793         if (node.hasAttribute('width')) {
22794             node.removeAttribute('width');
22795         }
22796         
22797          
22798         if (node.hasAttribute("style")) {
22799             // pretty basic...
22800             
22801             var styles = node.getAttribute("style").split(";");
22802             var nstyle = [];
22803             Roo.each(styles, function(s) {
22804                 if (!s.match(/:/)) {
22805                     return;
22806                 }
22807                 var kv = s.split(":");
22808                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22809                     return;
22810                 }
22811                 // what ever is left... we allow.
22812                 nstyle.push(s);
22813             });
22814             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22815             if (!nstyle.length) {
22816                 node.removeAttribute('style');
22817             }
22818         }
22819         
22820         this.iterateChildren(node, this.cleanTableWidths);
22821         
22822         
22823     },
22824     
22825     
22826     
22827     
22828     domToHTML : function(currentElement, depth, nopadtext) {
22829         
22830         depth = depth || 0;
22831         nopadtext = nopadtext || false;
22832     
22833         if (!currentElement) {
22834             return this.domToHTML(this.doc.body);
22835         }
22836         
22837         //Roo.log(currentElement);
22838         var j;
22839         var allText = false;
22840         var nodeName = currentElement.nodeName;
22841         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22842         
22843         if  (nodeName == '#text') {
22844             
22845             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22846         }
22847         
22848         
22849         var ret = '';
22850         if (nodeName != 'BODY') {
22851              
22852             var i = 0;
22853             // Prints the node tagName, such as <A>, <IMG>, etc
22854             if (tagName) {
22855                 var attr = [];
22856                 for(i = 0; i < currentElement.attributes.length;i++) {
22857                     // quoting?
22858                     var aname = currentElement.attributes.item(i).name;
22859                     if (!currentElement.attributes.item(i).value.length) {
22860                         continue;
22861                     }
22862                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22863                 }
22864                 
22865                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22866             } 
22867             else {
22868                 
22869                 // eack
22870             }
22871         } else {
22872             tagName = false;
22873         }
22874         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22875             return ret;
22876         }
22877         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22878             nopadtext = true;
22879         }
22880         
22881         
22882         // Traverse the tree
22883         i = 0;
22884         var currentElementChild = currentElement.childNodes.item(i);
22885         var allText = true;
22886         var innerHTML  = '';
22887         lastnode = '';
22888         while (currentElementChild) {
22889             // Formatting code (indent the tree so it looks nice on the screen)
22890             var nopad = nopadtext;
22891             if (lastnode == 'SPAN') {
22892                 nopad  = true;
22893             }
22894             // text
22895             if  (currentElementChild.nodeName == '#text') {
22896                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22897                 toadd = nopadtext ? toadd : toadd.trim();
22898                 if (!nopad && toadd.length > 80) {
22899                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22900                 }
22901                 innerHTML  += toadd;
22902                 
22903                 i++;
22904                 currentElementChild = currentElement.childNodes.item(i);
22905                 lastNode = '';
22906                 continue;
22907             }
22908             allText = false;
22909             
22910             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22911                 
22912             // Recursively traverse the tree structure of the child node
22913             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22914             lastnode = currentElementChild.nodeName;
22915             i++;
22916             currentElementChild=currentElement.childNodes.item(i);
22917         }
22918         
22919         ret += innerHTML;
22920         
22921         if (!allText) {
22922                 // The remaining code is mostly for formatting the tree
22923             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22924         }
22925         
22926         
22927         if (tagName) {
22928             ret+= "</"+tagName+">";
22929         }
22930         return ret;
22931         
22932     },
22933         
22934     applyBlacklists : function()
22935     {
22936         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22937         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22938         
22939         this.white = [];
22940         this.black = [];
22941         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22942             if (b.indexOf(tag) > -1) {
22943                 return;
22944             }
22945             this.white.push(tag);
22946             
22947         }, this);
22948         
22949         Roo.each(w, function(tag) {
22950             if (b.indexOf(tag) > -1) {
22951                 return;
22952             }
22953             if (this.white.indexOf(tag) > -1) {
22954                 return;
22955             }
22956             this.white.push(tag);
22957             
22958         }, this);
22959         
22960         
22961         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22962             if (w.indexOf(tag) > -1) {
22963                 return;
22964             }
22965             this.black.push(tag);
22966             
22967         }, this);
22968         
22969         Roo.each(b, function(tag) {
22970             if (w.indexOf(tag) > -1) {
22971                 return;
22972             }
22973             if (this.black.indexOf(tag) > -1) {
22974                 return;
22975             }
22976             this.black.push(tag);
22977             
22978         }, this);
22979         
22980         
22981         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22982         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22983         
22984         this.cwhite = [];
22985         this.cblack = [];
22986         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22987             if (b.indexOf(tag) > -1) {
22988                 return;
22989             }
22990             this.cwhite.push(tag);
22991             
22992         }, this);
22993         
22994         Roo.each(w, function(tag) {
22995             if (b.indexOf(tag) > -1) {
22996                 return;
22997             }
22998             if (this.cwhite.indexOf(tag) > -1) {
22999                 return;
23000             }
23001             this.cwhite.push(tag);
23002             
23003         }, this);
23004         
23005         
23006         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23007             if (w.indexOf(tag) > -1) {
23008                 return;
23009             }
23010             this.cblack.push(tag);
23011             
23012         }, this);
23013         
23014         Roo.each(b, function(tag) {
23015             if (w.indexOf(tag) > -1) {
23016                 return;
23017             }
23018             if (this.cblack.indexOf(tag) > -1) {
23019                 return;
23020             }
23021             this.cblack.push(tag);
23022             
23023         }, this);
23024     },
23025     
23026     setStylesheets : function(stylesheets)
23027     {
23028         if(typeof(stylesheets) == 'string'){
23029             Roo.get(this.iframe.contentDocument.head).createChild({
23030                 tag : 'link',
23031                 rel : 'stylesheet',
23032                 type : 'text/css',
23033                 href : stylesheets
23034             });
23035             
23036             return;
23037         }
23038         var _this = this;
23039      
23040         Roo.each(stylesheets, function(s) {
23041             if(!s.length){
23042                 return;
23043             }
23044             
23045             Roo.get(_this.iframe.contentDocument.head).createChild({
23046                 tag : 'link',
23047                 rel : 'stylesheet',
23048                 type : 'text/css',
23049                 href : s
23050             });
23051         });
23052
23053         
23054     },
23055     
23056     removeStylesheets : function()
23057     {
23058         var _this = this;
23059         
23060         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23061             s.remove();
23062         });
23063     },
23064     
23065     setStyle : function(style)
23066     {
23067         Roo.get(this.iframe.contentDocument.head).createChild({
23068             tag : 'style',
23069             type : 'text/css',
23070             html : style
23071         });
23072
23073         return;
23074     }
23075     
23076     // hide stuff that is not compatible
23077     /**
23078      * @event blur
23079      * @hide
23080      */
23081     /**
23082      * @event change
23083      * @hide
23084      */
23085     /**
23086      * @event focus
23087      * @hide
23088      */
23089     /**
23090      * @event specialkey
23091      * @hide
23092      */
23093     /**
23094      * @cfg {String} fieldClass @hide
23095      */
23096     /**
23097      * @cfg {String} focusClass @hide
23098      */
23099     /**
23100      * @cfg {String} autoCreate @hide
23101      */
23102     /**
23103      * @cfg {String} inputType @hide
23104      */
23105     /**
23106      * @cfg {String} invalidClass @hide
23107      */
23108     /**
23109      * @cfg {String} invalidText @hide
23110      */
23111     /**
23112      * @cfg {String} msgFx @hide
23113      */
23114     /**
23115      * @cfg {String} validateOnBlur @hide
23116      */
23117 });
23118
23119 Roo.HtmlEditorCore.white = [
23120         'area', 'br', 'img', 'input', 'hr', 'wbr',
23121         
23122        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23123        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23124        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23125        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23126        'table',   'ul',         'xmp', 
23127        
23128        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23129       'thead',   'tr', 
23130      
23131       'dir', 'menu', 'ol', 'ul', 'dl',
23132        
23133       'embed',  'object'
23134 ];
23135
23136
23137 Roo.HtmlEditorCore.black = [
23138     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23139         'applet', // 
23140         'base',   'basefont', 'bgsound', 'blink',  'body', 
23141         'frame',  'frameset', 'head',    'html',   'ilayer', 
23142         'iframe', 'layer',  'link',     'meta',    'object',   
23143         'script', 'style' ,'title',  'xml' // clean later..
23144 ];
23145 Roo.HtmlEditorCore.clean = [
23146     'script', 'style', 'title', 'xml'
23147 ];
23148 Roo.HtmlEditorCore.remove = [
23149     'font'
23150 ];
23151 // attributes..
23152
23153 Roo.HtmlEditorCore.ablack = [
23154     'on'
23155 ];
23156     
23157 Roo.HtmlEditorCore.aclean = [ 
23158     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23159 ];
23160
23161 // protocols..
23162 Roo.HtmlEditorCore.pwhite= [
23163         'http',  'https',  'mailto'
23164 ];
23165
23166 // white listed style attributes.
23167 Roo.HtmlEditorCore.cwhite= [
23168       //  'text-align', /// default is to allow most things..
23169       
23170          
23171 //        'font-size'//??
23172 ];
23173
23174 // black listed style attributes.
23175 Roo.HtmlEditorCore.cblack= [
23176       //  'font-size' -- this can be set by the project 
23177 ];
23178
23179
23180 Roo.HtmlEditorCore.swapCodes   =[ 
23181     [    8211, "--" ], 
23182     [    8212, "--" ], 
23183     [    8216,  "'" ],  
23184     [    8217, "'" ],  
23185     [    8220, '"' ],  
23186     [    8221, '"' ],  
23187     [    8226, "*" ],  
23188     [    8230, "..." ]
23189 ]; 
23190
23191     /*
23192  * - LGPL
23193  *
23194  * HtmlEditor
23195  * 
23196  */
23197
23198 /**
23199  * @class Roo.bootstrap.HtmlEditor
23200  * @extends Roo.bootstrap.TextArea
23201  * Bootstrap HtmlEditor class
23202
23203  * @constructor
23204  * Create a new HtmlEditor
23205  * @param {Object} config The config object
23206  */
23207
23208 Roo.bootstrap.HtmlEditor = function(config){
23209     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23210     if (!this.toolbars) {
23211         this.toolbars = [];
23212     }
23213     
23214     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23215     this.addEvents({
23216             /**
23217              * @event initialize
23218              * Fires when the editor is fully initialized (including the iframe)
23219              * @param {HtmlEditor} this
23220              */
23221             initialize: true,
23222             /**
23223              * @event activate
23224              * Fires when the editor is first receives the focus. Any insertion must wait
23225              * until after this event.
23226              * @param {HtmlEditor} this
23227              */
23228             activate: true,
23229              /**
23230              * @event beforesync
23231              * Fires before the textarea is updated with content from the editor iframe. Return false
23232              * to cancel the sync.
23233              * @param {HtmlEditor} this
23234              * @param {String} html
23235              */
23236             beforesync: true,
23237              /**
23238              * @event beforepush
23239              * Fires before the iframe editor is updated with content from the textarea. Return false
23240              * to cancel the push.
23241              * @param {HtmlEditor} this
23242              * @param {String} html
23243              */
23244             beforepush: true,
23245              /**
23246              * @event sync
23247              * Fires when the textarea is updated with content from the editor iframe.
23248              * @param {HtmlEditor} this
23249              * @param {String} html
23250              */
23251             sync: true,
23252              /**
23253              * @event push
23254              * Fires when the iframe editor is updated with content from the textarea.
23255              * @param {HtmlEditor} this
23256              * @param {String} html
23257              */
23258             push: true,
23259              /**
23260              * @event editmodechange
23261              * Fires when the editor switches edit modes
23262              * @param {HtmlEditor} this
23263              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23264              */
23265             editmodechange: true,
23266             /**
23267              * @event editorevent
23268              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23269              * @param {HtmlEditor} this
23270              */
23271             editorevent: true,
23272             /**
23273              * @event firstfocus
23274              * Fires when on first focus - needed by toolbars..
23275              * @param {HtmlEditor} this
23276              */
23277             firstfocus: true,
23278             /**
23279              * @event autosave
23280              * Auto save the htmlEditor value as a file into Events
23281              * @param {HtmlEditor} this
23282              */
23283             autosave: true,
23284             /**
23285              * @event savedpreview
23286              * preview the saved version of htmlEditor
23287              * @param {HtmlEditor} this
23288              */
23289             savedpreview: true
23290         });
23291 };
23292
23293
23294 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23295     
23296     
23297       /**
23298      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23299      */
23300     toolbars : false,
23301     
23302      /**
23303     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23304     */
23305     btns : [],
23306    
23307      /**
23308      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23309      *                        Roo.resizable.
23310      */
23311     resizable : false,
23312      /**
23313      * @cfg {Number} height (in pixels)
23314      */   
23315     height: 300,
23316    /**
23317      * @cfg {Number} width (in pixels)
23318      */   
23319     width: false,
23320     
23321     /**
23322      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23323      * 
23324      */
23325     stylesheets: false,
23326     
23327     // id of frame..
23328     frameId: false,
23329     
23330     // private properties
23331     validationEvent : false,
23332     deferHeight: true,
23333     initialized : false,
23334     activated : false,
23335     
23336     onFocus : Roo.emptyFn,
23337     iframePad:3,
23338     hideMode:'offsets',
23339     
23340     tbContainer : false,
23341     
23342     bodyCls : '',
23343     
23344     toolbarContainer :function() {
23345         return this.wrap.select('.x-html-editor-tb',true).first();
23346     },
23347
23348     /**
23349      * Protected method that will not generally be called directly. It
23350      * is called when the editor creates its toolbar. Override this method if you need to
23351      * add custom toolbar buttons.
23352      * @param {HtmlEditor} editor
23353      */
23354     createToolbar : function(){
23355         Roo.log('renewing');
23356         Roo.log("create toolbars");
23357         
23358         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23359         this.toolbars[0].render(this.toolbarContainer());
23360         
23361         return;
23362         
23363 //        if (!editor.toolbars || !editor.toolbars.length) {
23364 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23365 //        }
23366 //        
23367 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23368 //            editor.toolbars[i] = Roo.factory(
23369 //                    typeof(editor.toolbars[i]) == 'string' ?
23370 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23371 //                Roo.bootstrap.HtmlEditor);
23372 //            editor.toolbars[i].init(editor);
23373 //        }
23374     },
23375
23376      
23377     // private
23378     onRender : function(ct, position)
23379     {
23380        // Roo.log("Call onRender: " + this.xtype);
23381         var _t = this;
23382         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23383       
23384         this.wrap = this.inputEl().wrap({
23385             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23386         });
23387         
23388         this.editorcore.onRender(ct, position);
23389          
23390         if (this.resizable) {
23391             this.resizeEl = new Roo.Resizable(this.wrap, {
23392                 pinned : true,
23393                 wrap: true,
23394                 dynamic : true,
23395                 minHeight : this.height,
23396                 height: this.height,
23397                 handles : this.resizable,
23398                 width: this.width,
23399                 listeners : {
23400                     resize : function(r, w, h) {
23401                         _t.onResize(w,h); // -something
23402                     }
23403                 }
23404             });
23405             
23406         }
23407         this.createToolbar(this);
23408        
23409         
23410         if(!this.width && this.resizable){
23411             this.setSize(this.wrap.getSize());
23412         }
23413         if (this.resizeEl) {
23414             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23415             // should trigger onReize..
23416         }
23417         
23418     },
23419
23420     // private
23421     onResize : function(w, h)
23422     {
23423         Roo.log('resize: ' +w + ',' + h );
23424         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23425         var ew = false;
23426         var eh = false;
23427         
23428         if(this.inputEl() ){
23429             if(typeof w == 'number'){
23430                 var aw = w - this.wrap.getFrameWidth('lr');
23431                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23432                 ew = aw;
23433             }
23434             if(typeof h == 'number'){
23435                  var tbh = -11;  // fixme it needs to tool bar size!
23436                 for (var i =0; i < this.toolbars.length;i++) {
23437                     // fixme - ask toolbars for heights?
23438                     tbh += this.toolbars[i].el.getHeight();
23439                     //if (this.toolbars[i].footer) {
23440                     //    tbh += this.toolbars[i].footer.el.getHeight();
23441                     //}
23442                 }
23443               
23444                 
23445                 
23446                 
23447                 
23448                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23449                 ah -= 5; // knock a few pixes off for look..
23450                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23451                 var eh = ah;
23452             }
23453         }
23454         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23455         this.editorcore.onResize(ew,eh);
23456         
23457     },
23458
23459     /**
23460      * Toggles the editor between standard and source edit mode.
23461      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23462      */
23463     toggleSourceEdit : function(sourceEditMode)
23464     {
23465         this.editorcore.toggleSourceEdit(sourceEditMode);
23466         
23467         if(this.editorcore.sourceEditMode){
23468             Roo.log('editor - showing textarea');
23469             
23470 //            Roo.log('in');
23471 //            Roo.log(this.syncValue());
23472             this.syncValue();
23473             this.inputEl().removeClass(['hide', 'x-hidden']);
23474             this.inputEl().dom.removeAttribute('tabIndex');
23475             this.inputEl().focus();
23476         }else{
23477             Roo.log('editor - hiding textarea');
23478 //            Roo.log('out')
23479 //            Roo.log(this.pushValue()); 
23480             this.pushValue();
23481             
23482             this.inputEl().addClass(['hide', 'x-hidden']);
23483             this.inputEl().dom.setAttribute('tabIndex', -1);
23484             //this.deferFocus();
23485         }
23486          
23487         if(this.resizable){
23488             this.setSize(this.wrap.getSize());
23489         }
23490         
23491         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23492     },
23493  
23494     // private (for BoxComponent)
23495     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23496
23497     // private (for BoxComponent)
23498     getResizeEl : function(){
23499         return this.wrap;
23500     },
23501
23502     // private (for BoxComponent)
23503     getPositionEl : function(){
23504         return this.wrap;
23505     },
23506
23507     // private
23508     initEvents : function(){
23509         this.originalValue = this.getValue();
23510     },
23511
23512 //    /**
23513 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23514 //     * @method
23515 //     */
23516 //    markInvalid : Roo.emptyFn,
23517 //    /**
23518 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23519 //     * @method
23520 //     */
23521 //    clearInvalid : Roo.emptyFn,
23522
23523     setValue : function(v){
23524         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23525         this.editorcore.pushValue();
23526     },
23527
23528      
23529     // private
23530     deferFocus : function(){
23531         this.focus.defer(10, this);
23532     },
23533
23534     // doc'ed in Field
23535     focus : function(){
23536         this.editorcore.focus();
23537         
23538     },
23539       
23540
23541     // private
23542     onDestroy : function(){
23543         
23544         
23545         
23546         if(this.rendered){
23547             
23548             for (var i =0; i < this.toolbars.length;i++) {
23549                 // fixme - ask toolbars for heights?
23550                 this.toolbars[i].onDestroy();
23551             }
23552             
23553             this.wrap.dom.innerHTML = '';
23554             this.wrap.remove();
23555         }
23556     },
23557
23558     // private
23559     onFirstFocus : function(){
23560         //Roo.log("onFirstFocus");
23561         this.editorcore.onFirstFocus();
23562          for (var i =0; i < this.toolbars.length;i++) {
23563             this.toolbars[i].onFirstFocus();
23564         }
23565         
23566     },
23567     
23568     // private
23569     syncValue : function()
23570     {   
23571         this.editorcore.syncValue();
23572     },
23573     
23574     pushValue : function()
23575     {   
23576         this.editorcore.pushValue();
23577     }
23578      
23579     
23580     // hide stuff that is not compatible
23581     /**
23582      * @event blur
23583      * @hide
23584      */
23585     /**
23586      * @event change
23587      * @hide
23588      */
23589     /**
23590      * @event focus
23591      * @hide
23592      */
23593     /**
23594      * @event specialkey
23595      * @hide
23596      */
23597     /**
23598      * @cfg {String} fieldClass @hide
23599      */
23600     /**
23601      * @cfg {String} focusClass @hide
23602      */
23603     /**
23604      * @cfg {String} autoCreate @hide
23605      */
23606     /**
23607      * @cfg {String} inputType @hide
23608      */
23609     /**
23610      * @cfg {String} invalidClass @hide
23611      */
23612     /**
23613      * @cfg {String} invalidText @hide
23614      */
23615     /**
23616      * @cfg {String} msgFx @hide
23617      */
23618     /**
23619      * @cfg {String} validateOnBlur @hide
23620      */
23621 });
23622  
23623     
23624    
23625    
23626    
23627       
23628 Roo.namespace('Roo.bootstrap.htmleditor');
23629 /**
23630  * @class Roo.bootstrap.HtmlEditorToolbar1
23631  * Basic Toolbar
23632  * 
23633  * Usage:
23634  *
23635  new Roo.bootstrap.HtmlEditor({
23636     ....
23637     toolbars : [
23638         new Roo.bootstrap.HtmlEditorToolbar1({
23639             disable : { fonts: 1 , format: 1, ..., ... , ...],
23640             btns : [ .... ]
23641         })
23642     }
23643      
23644  * 
23645  * @cfg {Object} disable List of elements to disable..
23646  * @cfg {Array} btns List of additional buttons.
23647  * 
23648  * 
23649  * NEEDS Extra CSS? 
23650  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23651  */
23652  
23653 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23654 {
23655     
23656     Roo.apply(this, config);
23657     
23658     // default disabled, based on 'good practice'..
23659     this.disable = this.disable || {};
23660     Roo.applyIf(this.disable, {
23661         fontSize : true,
23662         colors : true,
23663         specialElements : true
23664     });
23665     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23666     
23667     this.editor = config.editor;
23668     this.editorcore = config.editor.editorcore;
23669     
23670     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23671     
23672     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23673     // dont call parent... till later.
23674 }
23675 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23676      
23677     bar : true,
23678     
23679     editor : false,
23680     editorcore : false,
23681     
23682     
23683     formats : [
23684         "p" ,  
23685         "h1","h2","h3","h4","h5","h6", 
23686         "pre", "code", 
23687         "abbr", "acronym", "address", "cite", "samp", "var",
23688         'div','span'
23689     ],
23690     
23691     onRender : function(ct, position)
23692     {
23693        // Roo.log("Call onRender: " + this.xtype);
23694         
23695        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23696        Roo.log(this.el);
23697        this.el.dom.style.marginBottom = '0';
23698        var _this = this;
23699        var editorcore = this.editorcore;
23700        var editor= this.editor;
23701        
23702        var children = [];
23703        var btn = function(id,cmd , toggle, handler, html){
23704        
23705             var  event = toggle ? 'toggle' : 'click';
23706        
23707             var a = {
23708                 size : 'sm',
23709                 xtype: 'Button',
23710                 xns: Roo.bootstrap,
23711                 glyphicon : id,
23712                 cmd : id || cmd,
23713                 enableToggle:toggle !== false,
23714                 html : html || '',
23715                 pressed : toggle ? false : null,
23716                 listeners : {}
23717             };
23718             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23719                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23720             };
23721             children.push(a);
23722             return a;
23723        }
23724        
23725     //    var cb_box = function...
23726         
23727         var style = {
23728                 xtype: 'Button',
23729                 size : 'sm',
23730                 xns: Roo.bootstrap,
23731                 glyphicon : 'font',
23732                 //html : 'submit'
23733                 menu : {
23734                     xtype: 'Menu',
23735                     xns: Roo.bootstrap,
23736                     items:  []
23737                 }
23738         };
23739         Roo.each(this.formats, function(f) {
23740             style.menu.items.push({
23741                 xtype :'MenuItem',
23742                 xns: Roo.bootstrap,
23743                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23744                 tagname : f,
23745                 listeners : {
23746                     click : function()
23747                     {
23748                         editorcore.insertTag(this.tagname);
23749                         editor.focus();
23750                     }
23751                 }
23752                 
23753             });
23754         });
23755         children.push(style);   
23756         
23757         btn('bold',false,true);
23758         btn('italic',false,true);
23759         btn('align-left', 'justifyleft',true);
23760         btn('align-center', 'justifycenter',true);
23761         btn('align-right' , 'justifyright',true);
23762         btn('link', false, false, function(btn) {
23763             //Roo.log("create link?");
23764             var url = prompt(this.createLinkText, this.defaultLinkValue);
23765             if(url && url != 'http:/'+'/'){
23766                 this.editorcore.relayCmd('createlink', url);
23767             }
23768         }),
23769         btn('list','insertunorderedlist',true);
23770         btn('pencil', false,true, function(btn){
23771                 Roo.log(this);
23772                 this.toggleSourceEdit(btn.pressed);
23773         });
23774         
23775         if (this.editor.btns.length > 0) {
23776             for (var i = 0; i<this.editor.btns.length; i++) {
23777                 children.push(this.editor.btns[i]);
23778             }
23779         }
23780         
23781         /*
23782         var cog = {
23783                 xtype: 'Button',
23784                 size : 'sm',
23785                 xns: Roo.bootstrap,
23786                 glyphicon : 'cog',
23787                 //html : 'submit'
23788                 menu : {
23789                     xtype: 'Menu',
23790                     xns: Roo.bootstrap,
23791                     items:  []
23792                 }
23793         };
23794         
23795         cog.menu.items.push({
23796             xtype :'MenuItem',
23797             xns: Roo.bootstrap,
23798             html : Clean styles,
23799             tagname : f,
23800             listeners : {
23801                 click : function()
23802                 {
23803                     editorcore.insertTag(this.tagname);
23804                     editor.focus();
23805                 }
23806             }
23807             
23808         });
23809        */
23810         
23811          
23812        this.xtype = 'NavSimplebar';
23813         
23814         for(var i=0;i< children.length;i++) {
23815             
23816             this.buttons.add(this.addxtypeChild(children[i]));
23817             
23818         }
23819         
23820         editor.on('editorevent', this.updateToolbar, this);
23821     },
23822     onBtnClick : function(id)
23823     {
23824        this.editorcore.relayCmd(id);
23825        this.editorcore.focus();
23826     },
23827     
23828     /**
23829      * Protected method that will not generally be called directly. It triggers
23830      * a toolbar update by reading the markup state of the current selection in the editor.
23831      */
23832     updateToolbar: function(){
23833
23834         if(!this.editorcore.activated){
23835             this.editor.onFirstFocus(); // is this neeed?
23836             return;
23837         }
23838
23839         var btns = this.buttons; 
23840         var doc = this.editorcore.doc;
23841         btns.get('bold').setActive(doc.queryCommandState('bold'));
23842         btns.get('italic').setActive(doc.queryCommandState('italic'));
23843         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23844         
23845         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23846         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23847         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23848         
23849         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23850         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23851          /*
23852         
23853         var ans = this.editorcore.getAllAncestors();
23854         if (this.formatCombo) {
23855             
23856             
23857             var store = this.formatCombo.store;
23858             this.formatCombo.setValue("");
23859             for (var i =0; i < ans.length;i++) {
23860                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23861                     // select it..
23862                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23863                     break;
23864                 }
23865             }
23866         }
23867         
23868         
23869         
23870         // hides menus... - so this cant be on a menu...
23871         Roo.bootstrap.MenuMgr.hideAll();
23872         */
23873         Roo.bootstrap.MenuMgr.hideAll();
23874         //this.editorsyncValue();
23875     },
23876     onFirstFocus: function() {
23877         this.buttons.each(function(item){
23878            item.enable();
23879         });
23880     },
23881     toggleSourceEdit : function(sourceEditMode){
23882         
23883           
23884         if(sourceEditMode){
23885             Roo.log("disabling buttons");
23886            this.buttons.each( function(item){
23887                 if(item.cmd != 'pencil'){
23888                     item.disable();
23889                 }
23890             });
23891           
23892         }else{
23893             Roo.log("enabling buttons");
23894             if(this.editorcore.initialized){
23895                 this.buttons.each( function(item){
23896                     item.enable();
23897                 });
23898             }
23899             
23900         }
23901         Roo.log("calling toggole on editor");
23902         // tell the editor that it's been pressed..
23903         this.editor.toggleSourceEdit(sourceEditMode);
23904        
23905     }
23906 });
23907
23908
23909
23910
23911
23912 /**
23913  * @class Roo.bootstrap.Table.AbstractSelectionModel
23914  * @extends Roo.util.Observable
23915  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23916  * implemented by descendant classes.  This class should not be directly instantiated.
23917  * @constructor
23918  */
23919 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23920     this.locked = false;
23921     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23922 };
23923
23924
23925 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23926     /** @ignore Called by the grid automatically. Do not call directly. */
23927     init : function(grid){
23928         this.grid = grid;
23929         this.initEvents();
23930     },
23931
23932     /**
23933      * Locks the selections.
23934      */
23935     lock : function(){
23936         this.locked = true;
23937     },
23938
23939     /**
23940      * Unlocks the selections.
23941      */
23942     unlock : function(){
23943         this.locked = false;
23944     },
23945
23946     /**
23947      * Returns true if the selections are locked.
23948      * @return {Boolean}
23949      */
23950     isLocked : function(){
23951         return this.locked;
23952     }
23953 });
23954 /**
23955  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23956  * @class Roo.bootstrap.Table.RowSelectionModel
23957  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23958  * It supports multiple selections and keyboard selection/navigation. 
23959  * @constructor
23960  * @param {Object} config
23961  */
23962
23963 Roo.bootstrap.Table.RowSelectionModel = function(config){
23964     Roo.apply(this, config);
23965     this.selections = new Roo.util.MixedCollection(false, function(o){
23966         return o.id;
23967     });
23968
23969     this.last = false;
23970     this.lastActive = false;
23971
23972     this.addEvents({
23973         /**
23974              * @event selectionchange
23975              * Fires when the selection changes
23976              * @param {SelectionModel} this
23977              */
23978             "selectionchange" : true,
23979         /**
23980              * @event afterselectionchange
23981              * Fires after the selection changes (eg. by key press or clicking)
23982              * @param {SelectionModel} this
23983              */
23984             "afterselectionchange" : true,
23985         /**
23986              * @event beforerowselect
23987              * Fires when a row is selected being selected, return false to cancel.
23988              * @param {SelectionModel} this
23989              * @param {Number} rowIndex The selected index
23990              * @param {Boolean} keepExisting False if other selections will be cleared
23991              */
23992             "beforerowselect" : true,
23993         /**
23994              * @event rowselect
23995              * Fires when a row is selected.
23996              * @param {SelectionModel} this
23997              * @param {Number} rowIndex The selected index
23998              * @param {Roo.data.Record} r The record
23999              */
24000             "rowselect" : true,
24001         /**
24002              * @event rowdeselect
24003              * Fires when a row is deselected.
24004              * @param {SelectionModel} this
24005              * @param {Number} rowIndex The selected index
24006              */
24007         "rowdeselect" : true
24008     });
24009     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24010     this.locked = false;
24011  };
24012
24013 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24014     /**
24015      * @cfg {Boolean} singleSelect
24016      * True to allow selection of only one row at a time (defaults to false)
24017      */
24018     singleSelect : false,
24019
24020     // private
24021     initEvents : function()
24022     {
24023
24024         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24025         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24026         //}else{ // allow click to work like normal
24027          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24028         //}
24029         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24030         this.grid.on("rowclick", this.handleMouseDown, this);
24031         
24032         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24033             "up" : function(e){
24034                 if(!e.shiftKey){
24035                     this.selectPrevious(e.shiftKey);
24036                 }else if(this.last !== false && this.lastActive !== false){
24037                     var last = this.last;
24038                     this.selectRange(this.last,  this.lastActive-1);
24039                     this.grid.getView().focusRow(this.lastActive);
24040                     if(last !== false){
24041                         this.last = last;
24042                     }
24043                 }else{
24044                     this.selectFirstRow();
24045                 }
24046                 this.fireEvent("afterselectionchange", this);
24047             },
24048             "down" : function(e){
24049                 if(!e.shiftKey){
24050                     this.selectNext(e.shiftKey);
24051                 }else if(this.last !== false && this.lastActive !== false){
24052                     var last = this.last;
24053                     this.selectRange(this.last,  this.lastActive+1);
24054                     this.grid.getView().focusRow(this.lastActive);
24055                     if(last !== false){
24056                         this.last = last;
24057                     }
24058                 }else{
24059                     this.selectFirstRow();
24060                 }
24061                 this.fireEvent("afterselectionchange", this);
24062             },
24063             scope: this
24064         });
24065         this.grid.store.on('load', function(){
24066             this.selections.clear();
24067         },this);
24068         /*
24069         var view = this.grid.view;
24070         view.on("refresh", this.onRefresh, this);
24071         view.on("rowupdated", this.onRowUpdated, this);
24072         view.on("rowremoved", this.onRemove, this);
24073         */
24074     },
24075
24076     // private
24077     onRefresh : function()
24078     {
24079         var ds = this.grid.store, i, v = this.grid.view;
24080         var s = this.selections;
24081         s.each(function(r){
24082             if((i = ds.indexOfId(r.id)) != -1){
24083                 v.onRowSelect(i);
24084             }else{
24085                 s.remove(r);
24086             }
24087         });
24088     },
24089
24090     // private
24091     onRemove : function(v, index, r){
24092         this.selections.remove(r);
24093     },
24094
24095     // private
24096     onRowUpdated : function(v, index, r){
24097         if(this.isSelected(r)){
24098             v.onRowSelect(index);
24099         }
24100     },
24101
24102     /**
24103      * Select records.
24104      * @param {Array} records The records to select
24105      * @param {Boolean} keepExisting (optional) True to keep existing selections
24106      */
24107     selectRecords : function(records, keepExisting)
24108     {
24109         if(!keepExisting){
24110             this.clearSelections();
24111         }
24112             var ds = this.grid.store;
24113         for(var i = 0, len = records.length; i < len; i++){
24114             this.selectRow(ds.indexOf(records[i]), true);
24115         }
24116     },
24117
24118     /**
24119      * Gets the number of selected rows.
24120      * @return {Number}
24121      */
24122     getCount : function(){
24123         return this.selections.length;
24124     },
24125
24126     /**
24127      * Selects the first row in the grid.
24128      */
24129     selectFirstRow : function(){
24130         this.selectRow(0);
24131     },
24132
24133     /**
24134      * Select the last row.
24135      * @param {Boolean} keepExisting (optional) True to keep existing selections
24136      */
24137     selectLastRow : function(keepExisting){
24138         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24139         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24140     },
24141
24142     /**
24143      * Selects the row immediately following the last selected row.
24144      * @param {Boolean} keepExisting (optional) True to keep existing selections
24145      */
24146     selectNext : function(keepExisting)
24147     {
24148             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24149             this.selectRow(this.last+1, keepExisting);
24150             this.grid.getView().focusRow(this.last);
24151         }
24152     },
24153
24154     /**
24155      * Selects the row that precedes the last selected row.
24156      * @param {Boolean} keepExisting (optional) True to keep existing selections
24157      */
24158     selectPrevious : function(keepExisting){
24159         if(this.last){
24160             this.selectRow(this.last-1, keepExisting);
24161             this.grid.getView().focusRow(this.last);
24162         }
24163     },
24164
24165     /**
24166      * Returns the selected records
24167      * @return {Array} Array of selected records
24168      */
24169     getSelections : function(){
24170         return [].concat(this.selections.items);
24171     },
24172
24173     /**
24174      * Returns the first selected record.
24175      * @return {Record}
24176      */
24177     getSelected : function(){
24178         return this.selections.itemAt(0);
24179     },
24180
24181
24182     /**
24183      * Clears all selections.
24184      */
24185     clearSelections : function(fast)
24186     {
24187         if(this.locked) {
24188             return;
24189         }
24190         if(fast !== true){
24191                 var ds = this.grid.store;
24192             var s = this.selections;
24193             s.each(function(r){
24194                 this.deselectRow(ds.indexOfId(r.id));
24195             }, this);
24196             s.clear();
24197         }else{
24198             this.selections.clear();
24199         }
24200         this.last = false;
24201     },
24202
24203
24204     /**
24205      * Selects all rows.
24206      */
24207     selectAll : function(){
24208         if(this.locked) {
24209             return;
24210         }
24211         this.selections.clear();
24212         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24213             this.selectRow(i, true);
24214         }
24215     },
24216
24217     /**
24218      * Returns True if there is a selection.
24219      * @return {Boolean}
24220      */
24221     hasSelection : function(){
24222         return this.selections.length > 0;
24223     },
24224
24225     /**
24226      * Returns True if the specified row is selected.
24227      * @param {Number/Record} record The record or index of the record to check
24228      * @return {Boolean}
24229      */
24230     isSelected : function(index){
24231             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24232         return (r && this.selections.key(r.id) ? true : false);
24233     },
24234
24235     /**
24236      * Returns True if the specified record id is selected.
24237      * @param {String} id The id of record to check
24238      * @return {Boolean}
24239      */
24240     isIdSelected : function(id){
24241         return (this.selections.key(id) ? true : false);
24242     },
24243
24244
24245     // private
24246     handleMouseDBClick : function(e, t){
24247         
24248     },
24249     // private
24250     handleMouseDown : function(e, t)
24251     {
24252             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24253         if(this.isLocked() || rowIndex < 0 ){
24254             return;
24255         };
24256         if(e.shiftKey && this.last !== false){
24257             var last = this.last;
24258             this.selectRange(last, rowIndex, e.ctrlKey);
24259             this.last = last; // reset the last
24260             t.focus();
24261     
24262         }else{
24263             var isSelected = this.isSelected(rowIndex);
24264             //Roo.log("select row:" + rowIndex);
24265             if(isSelected){
24266                 this.deselectRow(rowIndex);
24267             } else {
24268                         this.selectRow(rowIndex, true);
24269             }
24270     
24271             /*
24272                 if(e.button !== 0 && isSelected){
24273                 alert('rowIndex 2: ' + rowIndex);
24274                     view.focusRow(rowIndex);
24275                 }else if(e.ctrlKey && isSelected){
24276                     this.deselectRow(rowIndex);
24277                 }else if(!isSelected){
24278                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24279                     view.focusRow(rowIndex);
24280                 }
24281             */
24282         }
24283         this.fireEvent("afterselectionchange", this);
24284     },
24285     // private
24286     handleDragableRowClick :  function(grid, rowIndex, e) 
24287     {
24288         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24289             this.selectRow(rowIndex, false);
24290             grid.view.focusRow(rowIndex);
24291              this.fireEvent("afterselectionchange", this);
24292         }
24293     },
24294     
24295     /**
24296      * Selects multiple rows.
24297      * @param {Array} rows Array of the indexes of the row to select
24298      * @param {Boolean} keepExisting (optional) True to keep existing selections
24299      */
24300     selectRows : function(rows, keepExisting){
24301         if(!keepExisting){
24302             this.clearSelections();
24303         }
24304         for(var i = 0, len = rows.length; i < len; i++){
24305             this.selectRow(rows[i], true);
24306         }
24307     },
24308
24309     /**
24310      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24311      * @param {Number} startRow The index of the first row in the range
24312      * @param {Number} endRow The index of the last row in the range
24313      * @param {Boolean} keepExisting (optional) True to retain existing selections
24314      */
24315     selectRange : function(startRow, endRow, keepExisting){
24316         if(this.locked) {
24317             return;
24318         }
24319         if(!keepExisting){
24320             this.clearSelections();
24321         }
24322         if(startRow <= endRow){
24323             for(var i = startRow; i <= endRow; i++){
24324                 this.selectRow(i, true);
24325             }
24326         }else{
24327             for(var i = startRow; i >= endRow; i--){
24328                 this.selectRow(i, true);
24329             }
24330         }
24331     },
24332
24333     /**
24334      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24335      * @param {Number} startRow The index of the first row in the range
24336      * @param {Number} endRow The index of the last row in the range
24337      */
24338     deselectRange : function(startRow, endRow, preventViewNotify){
24339         if(this.locked) {
24340             return;
24341         }
24342         for(var i = startRow; i <= endRow; i++){
24343             this.deselectRow(i, preventViewNotify);
24344         }
24345     },
24346
24347     /**
24348      * Selects a row.
24349      * @param {Number} row The index of the row to select
24350      * @param {Boolean} keepExisting (optional) True to keep existing selections
24351      */
24352     selectRow : function(index, keepExisting, preventViewNotify)
24353     {
24354             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24355             return;
24356         }
24357         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24358             if(!keepExisting || this.singleSelect){
24359                 this.clearSelections();
24360             }
24361             
24362             var r = this.grid.store.getAt(index);
24363             //console.log('selectRow - record id :' + r.id);
24364             
24365             this.selections.add(r);
24366             this.last = this.lastActive = index;
24367             if(!preventViewNotify){
24368                 var proxy = new Roo.Element(
24369                                 this.grid.getRowDom(index)
24370                 );
24371                 proxy.addClass('bg-info info');
24372             }
24373             this.fireEvent("rowselect", this, index, r);
24374             this.fireEvent("selectionchange", this);
24375         }
24376     },
24377
24378     /**
24379      * Deselects a row.
24380      * @param {Number} row The index of the row to deselect
24381      */
24382     deselectRow : function(index, preventViewNotify)
24383     {
24384         if(this.locked) {
24385             return;
24386         }
24387         if(this.last == index){
24388             this.last = false;
24389         }
24390         if(this.lastActive == index){
24391             this.lastActive = false;
24392         }
24393         
24394         var r = this.grid.store.getAt(index);
24395         if (!r) {
24396             return;
24397         }
24398         
24399         this.selections.remove(r);
24400         //.console.log('deselectRow - record id :' + r.id);
24401         if(!preventViewNotify){
24402         
24403             var proxy = new Roo.Element(
24404                 this.grid.getRowDom(index)
24405             );
24406             proxy.removeClass('bg-info info');
24407         }
24408         this.fireEvent("rowdeselect", this, index);
24409         this.fireEvent("selectionchange", this);
24410     },
24411
24412     // private
24413     restoreLast : function(){
24414         if(this._last){
24415             this.last = this._last;
24416         }
24417     },
24418
24419     // private
24420     acceptsNav : function(row, col, cm){
24421         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24422     },
24423
24424     // private
24425     onEditorKey : function(field, e){
24426         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24427         if(k == e.TAB){
24428             e.stopEvent();
24429             ed.completeEdit();
24430             if(e.shiftKey){
24431                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24432             }else{
24433                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24434             }
24435         }else if(k == e.ENTER && !e.ctrlKey){
24436             e.stopEvent();
24437             ed.completeEdit();
24438             if(e.shiftKey){
24439                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24440             }else{
24441                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24442             }
24443         }else if(k == e.ESC){
24444             ed.cancelEdit();
24445         }
24446         if(newCell){
24447             g.startEditing(newCell[0], newCell[1]);
24448         }
24449     }
24450 });
24451 /*
24452  * Based on:
24453  * Ext JS Library 1.1.1
24454  * Copyright(c) 2006-2007, Ext JS, LLC.
24455  *
24456  * Originally Released Under LGPL - original licence link has changed is not relivant.
24457  *
24458  * Fork - LGPL
24459  * <script type="text/javascript">
24460  */
24461  
24462 /**
24463  * @class Roo.bootstrap.PagingToolbar
24464  * @extends Roo.bootstrap.NavSimplebar
24465  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24466  * @constructor
24467  * Create a new PagingToolbar
24468  * @param {Object} config The config object
24469  * @param {Roo.data.Store} store
24470  */
24471 Roo.bootstrap.PagingToolbar = function(config)
24472 {
24473     // old args format still supported... - xtype is prefered..
24474         // created from xtype...
24475     
24476     this.ds = config.dataSource;
24477     
24478     if (config.store && !this.ds) {
24479         this.store= Roo.factory(config.store, Roo.data);
24480         this.ds = this.store;
24481         this.ds.xmodule = this.xmodule || false;
24482     }
24483     
24484     this.toolbarItems = [];
24485     if (config.items) {
24486         this.toolbarItems = config.items;
24487     }
24488     
24489     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24490     
24491     this.cursor = 0;
24492     
24493     if (this.ds) { 
24494         this.bind(this.ds);
24495     }
24496     
24497     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24498     
24499 };
24500
24501 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24502     /**
24503      * @cfg {Roo.data.Store} dataSource
24504      * The underlying data store providing the paged data
24505      */
24506     /**
24507      * @cfg {String/HTMLElement/Element} container
24508      * container The id or element that will contain the toolbar
24509      */
24510     /**
24511      * @cfg {Boolean} displayInfo
24512      * True to display the displayMsg (defaults to false)
24513      */
24514     /**
24515      * @cfg {Number} pageSize
24516      * The number of records to display per page (defaults to 20)
24517      */
24518     pageSize: 20,
24519     /**
24520      * @cfg {String} displayMsg
24521      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24522      */
24523     displayMsg : 'Displaying {0} - {1} of {2}',
24524     /**
24525      * @cfg {String} emptyMsg
24526      * The message to display when no records are found (defaults to "No data to display")
24527      */
24528     emptyMsg : 'No data to display',
24529     /**
24530      * Customizable piece of the default paging text (defaults to "Page")
24531      * @type String
24532      */
24533     beforePageText : "Page",
24534     /**
24535      * Customizable piece of the default paging text (defaults to "of %0")
24536      * @type String
24537      */
24538     afterPageText : "of {0}",
24539     /**
24540      * Customizable piece of the default paging text (defaults to "First Page")
24541      * @type String
24542      */
24543     firstText : "First Page",
24544     /**
24545      * Customizable piece of the default paging text (defaults to "Previous Page")
24546      * @type String
24547      */
24548     prevText : "Previous Page",
24549     /**
24550      * Customizable piece of the default paging text (defaults to "Next Page")
24551      * @type String
24552      */
24553     nextText : "Next Page",
24554     /**
24555      * Customizable piece of the default paging text (defaults to "Last Page")
24556      * @type String
24557      */
24558     lastText : "Last Page",
24559     /**
24560      * Customizable piece of the default paging text (defaults to "Refresh")
24561      * @type String
24562      */
24563     refreshText : "Refresh",
24564
24565     buttons : false,
24566     // private
24567     onRender : function(ct, position) 
24568     {
24569         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24570         this.navgroup.parentId = this.id;
24571         this.navgroup.onRender(this.el, null);
24572         // add the buttons to the navgroup
24573         
24574         if(this.displayInfo){
24575             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24576             this.displayEl = this.el.select('.x-paging-info', true).first();
24577 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24578 //            this.displayEl = navel.el.select('span',true).first();
24579         }
24580         
24581         var _this = this;
24582         
24583         if(this.buttons){
24584             Roo.each(_this.buttons, function(e){ // this might need to use render????
24585                Roo.factory(e).render(_this.el);
24586             });
24587         }
24588             
24589         Roo.each(_this.toolbarItems, function(e) {
24590             _this.navgroup.addItem(e);
24591         });
24592         
24593         
24594         this.first = this.navgroup.addItem({
24595             tooltip: this.firstText,
24596             cls: "prev",
24597             icon : 'fa fa-backward',
24598             disabled: true,
24599             preventDefault: true,
24600             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24601         });
24602         
24603         this.prev =  this.navgroup.addItem({
24604             tooltip: this.prevText,
24605             cls: "prev",
24606             icon : 'fa fa-step-backward',
24607             disabled: true,
24608             preventDefault: true,
24609             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24610         });
24611     //this.addSeparator();
24612         
24613         
24614         var field = this.navgroup.addItem( {
24615             tagtype : 'span',
24616             cls : 'x-paging-position',
24617             
24618             html : this.beforePageText  +
24619                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24620                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24621          } ); //?? escaped?
24622         
24623         this.field = field.el.select('input', true).first();
24624         this.field.on("keydown", this.onPagingKeydown, this);
24625         this.field.on("focus", function(){this.dom.select();});
24626     
24627     
24628         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24629         //this.field.setHeight(18);
24630         //this.addSeparator();
24631         this.next = this.navgroup.addItem({
24632             tooltip: this.nextText,
24633             cls: "next",
24634             html : ' <i class="fa fa-step-forward">',
24635             disabled: true,
24636             preventDefault: true,
24637             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24638         });
24639         this.last = this.navgroup.addItem({
24640             tooltip: this.lastText,
24641             icon : 'fa fa-forward',
24642             cls: "next",
24643             disabled: true,
24644             preventDefault: true,
24645             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24646         });
24647     //this.addSeparator();
24648         this.loading = this.navgroup.addItem({
24649             tooltip: this.refreshText,
24650             icon: 'fa fa-refresh',
24651             preventDefault: true,
24652             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24653         });
24654         
24655     },
24656
24657     // private
24658     updateInfo : function(){
24659         if(this.displayEl){
24660             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24661             var msg = count == 0 ?
24662                 this.emptyMsg :
24663                 String.format(
24664                     this.displayMsg,
24665                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24666                 );
24667             this.displayEl.update(msg);
24668         }
24669     },
24670
24671     // private
24672     onLoad : function(ds, r, o)
24673     {
24674         this.cursor = o.params.start ? o.params.start : 0;
24675         
24676         var d = this.getPageData(),
24677             ap = d.activePage,
24678             ps = d.pages;
24679         
24680         
24681         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24682         this.field.dom.value = ap;
24683         this.first.setDisabled(ap == 1);
24684         this.prev.setDisabled(ap == 1);
24685         this.next.setDisabled(ap == ps);
24686         this.last.setDisabled(ap == ps);
24687         this.loading.enable();
24688         this.updateInfo();
24689     },
24690
24691     // private
24692     getPageData : function(){
24693         var total = this.ds.getTotalCount();
24694         return {
24695             total : total,
24696             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24697             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24698         };
24699     },
24700
24701     // private
24702     onLoadError : function(){
24703         this.loading.enable();
24704     },
24705
24706     // private
24707     onPagingKeydown : function(e){
24708         var k = e.getKey();
24709         var d = this.getPageData();
24710         if(k == e.RETURN){
24711             var v = this.field.dom.value, pageNum;
24712             if(!v || isNaN(pageNum = parseInt(v, 10))){
24713                 this.field.dom.value = d.activePage;
24714                 return;
24715             }
24716             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24717             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24718             e.stopEvent();
24719         }
24720         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))
24721         {
24722           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24723           this.field.dom.value = pageNum;
24724           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24725           e.stopEvent();
24726         }
24727         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24728         {
24729           var v = this.field.dom.value, pageNum; 
24730           var increment = (e.shiftKey) ? 10 : 1;
24731           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24732                 increment *= -1;
24733           }
24734           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24735             this.field.dom.value = d.activePage;
24736             return;
24737           }
24738           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24739           {
24740             this.field.dom.value = parseInt(v, 10) + increment;
24741             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24742             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24743           }
24744           e.stopEvent();
24745         }
24746     },
24747
24748     // private
24749     beforeLoad : function(){
24750         if(this.loading){
24751             this.loading.disable();
24752         }
24753     },
24754
24755     // private
24756     onClick : function(which){
24757         
24758         var ds = this.ds;
24759         if (!ds) {
24760             return;
24761         }
24762         
24763         switch(which){
24764             case "first":
24765                 ds.load({params:{start: 0, limit: this.pageSize}});
24766             break;
24767             case "prev":
24768                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24769             break;
24770             case "next":
24771                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24772             break;
24773             case "last":
24774                 var total = ds.getTotalCount();
24775                 var extra = total % this.pageSize;
24776                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24777                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24778             break;
24779             case "refresh":
24780                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24781             break;
24782         }
24783     },
24784
24785     /**
24786      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24787      * @param {Roo.data.Store} store The data store to unbind
24788      */
24789     unbind : function(ds){
24790         ds.un("beforeload", this.beforeLoad, this);
24791         ds.un("load", this.onLoad, this);
24792         ds.un("loadexception", this.onLoadError, this);
24793         ds.un("remove", this.updateInfo, this);
24794         ds.un("add", this.updateInfo, this);
24795         this.ds = undefined;
24796     },
24797
24798     /**
24799      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24800      * @param {Roo.data.Store} store The data store to bind
24801      */
24802     bind : function(ds){
24803         ds.on("beforeload", this.beforeLoad, this);
24804         ds.on("load", this.onLoad, this);
24805         ds.on("loadexception", this.onLoadError, this);
24806         ds.on("remove", this.updateInfo, this);
24807         ds.on("add", this.updateInfo, this);
24808         this.ds = ds;
24809     }
24810 });/*
24811  * - LGPL
24812  *
24813  * element
24814  * 
24815  */
24816
24817 /**
24818  * @class Roo.bootstrap.MessageBar
24819  * @extends Roo.bootstrap.Component
24820  * Bootstrap MessageBar class
24821  * @cfg {String} html contents of the MessageBar
24822  * @cfg {String} weight (info | success | warning | danger) default info
24823  * @cfg {String} beforeClass insert the bar before the given class
24824  * @cfg {Boolean} closable (true | false) default false
24825  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24826  * 
24827  * @constructor
24828  * Create a new Element
24829  * @param {Object} config The config object
24830  */
24831
24832 Roo.bootstrap.MessageBar = function(config){
24833     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24834 };
24835
24836 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24837     
24838     html: '',
24839     weight: 'info',
24840     closable: false,
24841     fixed: false,
24842     beforeClass: 'bootstrap-sticky-wrap',
24843     
24844     getAutoCreate : function(){
24845         
24846         var cfg = {
24847             tag: 'div',
24848             cls: 'alert alert-dismissable alert-' + this.weight,
24849             cn: [
24850                 {
24851                     tag: 'span',
24852                     cls: 'message',
24853                     html: this.html || ''
24854                 }
24855             ]
24856         };
24857         
24858         if(this.fixed){
24859             cfg.cls += ' alert-messages-fixed';
24860         }
24861         
24862         if(this.closable){
24863             cfg.cn.push({
24864                 tag: 'button',
24865                 cls: 'close',
24866                 html: 'x'
24867             });
24868         }
24869         
24870         return cfg;
24871     },
24872     
24873     onRender : function(ct, position)
24874     {
24875         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24876         
24877         if(!this.el){
24878             var cfg = Roo.apply({},  this.getAutoCreate());
24879             cfg.id = Roo.id();
24880             
24881             if (this.cls) {
24882                 cfg.cls += ' ' + this.cls;
24883             }
24884             if (this.style) {
24885                 cfg.style = this.style;
24886             }
24887             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24888             
24889             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24890         }
24891         
24892         this.el.select('>button.close').on('click', this.hide, this);
24893         
24894     },
24895     
24896     show : function()
24897     {
24898         if (!this.rendered) {
24899             this.render();
24900         }
24901         
24902         this.el.show();
24903         
24904         this.fireEvent('show', this);
24905         
24906     },
24907     
24908     hide : function()
24909     {
24910         if (!this.rendered) {
24911             this.render();
24912         }
24913         
24914         this.el.hide();
24915         
24916         this.fireEvent('hide', this);
24917     },
24918     
24919     update : function()
24920     {
24921 //        var e = this.el.dom.firstChild;
24922 //        
24923 //        if(this.closable){
24924 //            e = e.nextSibling;
24925 //        }
24926 //        
24927 //        e.data = this.html || '';
24928
24929         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24930     }
24931    
24932 });
24933
24934  
24935
24936      /*
24937  * - LGPL
24938  *
24939  * Graph
24940  * 
24941  */
24942
24943
24944 /**
24945  * @class Roo.bootstrap.Graph
24946  * @extends Roo.bootstrap.Component
24947  * Bootstrap Graph class
24948 > Prameters
24949  -sm {number} sm 4
24950  -md {number} md 5
24951  @cfg {String} graphtype  bar | vbar | pie
24952  @cfg {number} g_x coodinator | centre x (pie)
24953  @cfg {number} g_y coodinator | centre y (pie)
24954  @cfg {number} g_r radius (pie)
24955  @cfg {number} g_height height of the chart (respected by all elements in the set)
24956  @cfg {number} g_width width of the chart (respected by all elements in the set)
24957  @cfg {Object} title The title of the chart
24958     
24959  -{Array}  values
24960  -opts (object) options for the chart 
24961      o {
24962      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24963      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24964      o vgutter (number)
24965      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.
24966      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24967      o to
24968      o stretch (boolean)
24969      o }
24970  -opts (object) options for the pie
24971      o{
24972      o cut
24973      o startAngle (number)
24974      o endAngle (number)
24975      } 
24976  *
24977  * @constructor
24978  * Create a new Input
24979  * @param {Object} config The config object
24980  */
24981
24982 Roo.bootstrap.Graph = function(config){
24983     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24984     
24985     this.addEvents({
24986         // img events
24987         /**
24988          * @event click
24989          * The img click event for the img.
24990          * @param {Roo.EventObject} e
24991          */
24992         "click" : true
24993     });
24994 };
24995
24996 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24997     
24998     sm: 4,
24999     md: 5,
25000     graphtype: 'bar',
25001     g_height: 250,
25002     g_width: 400,
25003     g_x: 50,
25004     g_y: 50,
25005     g_r: 30,
25006     opts:{
25007         //g_colors: this.colors,
25008         g_type: 'soft',
25009         g_gutter: '20%'
25010
25011     },
25012     title : false,
25013
25014     getAutoCreate : function(){
25015         
25016         var cfg = {
25017             tag: 'div',
25018             html : null
25019         };
25020         
25021         
25022         return  cfg;
25023     },
25024
25025     onRender : function(ct,position){
25026         
25027         
25028         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25029         
25030         if (typeof(Raphael) == 'undefined') {
25031             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25032             return;
25033         }
25034         
25035         this.raphael = Raphael(this.el.dom);
25036         
25037                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25038                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25039                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25040                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25041                 /*
25042                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25043                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25044                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25045                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25046                 
25047                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25048                 r.barchart(330, 10, 300, 220, data1);
25049                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25050                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25051                 */
25052                 
25053                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25054                 // r.barchart(30, 30, 560, 250,  xdata, {
25055                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25056                 //     axis : "0 0 1 1",
25057                 //     axisxlabels :  xdata
25058                 //     //yvalues : cols,
25059                    
25060                 // });
25061 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25062 //        
25063 //        this.load(null,xdata,{
25064 //                axis : "0 0 1 1",
25065 //                axisxlabels :  xdata
25066 //                });
25067
25068     },
25069
25070     load : function(graphtype,xdata,opts)
25071     {
25072         this.raphael.clear();
25073         if(!graphtype) {
25074             graphtype = this.graphtype;
25075         }
25076         if(!opts){
25077             opts = this.opts;
25078         }
25079         var r = this.raphael,
25080             fin = function () {
25081                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25082             },
25083             fout = function () {
25084                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25085             },
25086             pfin = function() {
25087                 this.sector.stop();
25088                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25089
25090                 if (this.label) {
25091                     this.label[0].stop();
25092                     this.label[0].attr({ r: 7.5 });
25093                     this.label[1].attr({ "font-weight": 800 });
25094                 }
25095             },
25096             pfout = function() {
25097                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25098
25099                 if (this.label) {
25100                     this.label[0].animate({ r: 5 }, 500, "bounce");
25101                     this.label[1].attr({ "font-weight": 400 });
25102                 }
25103             };
25104
25105         switch(graphtype){
25106             case 'bar':
25107                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25108                 break;
25109             case 'hbar':
25110                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25111                 break;
25112             case 'pie':
25113 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25114 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25115 //            
25116                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25117                 
25118                 break;
25119
25120         }
25121         
25122         if(this.title){
25123             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25124         }
25125         
25126     },
25127     
25128     setTitle: function(o)
25129     {
25130         this.title = o;
25131     },
25132     
25133     initEvents: function() {
25134         
25135         if(!this.href){
25136             this.el.on('click', this.onClick, this);
25137         }
25138     },
25139     
25140     onClick : function(e)
25141     {
25142         Roo.log('img onclick');
25143         this.fireEvent('click', this, e);
25144     }
25145    
25146 });
25147
25148  
25149 /*
25150  * - LGPL
25151  *
25152  * numberBox
25153  * 
25154  */
25155 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25156
25157 /**
25158  * @class Roo.bootstrap.dash.NumberBox
25159  * @extends Roo.bootstrap.Component
25160  * Bootstrap NumberBox class
25161  * @cfg {String} headline Box headline
25162  * @cfg {String} content Box content
25163  * @cfg {String} icon Box icon
25164  * @cfg {String} footer Footer text
25165  * @cfg {String} fhref Footer href
25166  * 
25167  * @constructor
25168  * Create a new NumberBox
25169  * @param {Object} config The config object
25170  */
25171
25172
25173 Roo.bootstrap.dash.NumberBox = function(config){
25174     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25175     
25176 };
25177
25178 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25179     
25180     headline : '',
25181     content : '',
25182     icon : '',
25183     footer : '',
25184     fhref : '',
25185     ficon : '',
25186     
25187     getAutoCreate : function(){
25188         
25189         var cfg = {
25190             tag : 'div',
25191             cls : 'small-box ',
25192             cn : [
25193                 {
25194                     tag : 'div',
25195                     cls : 'inner',
25196                     cn :[
25197                         {
25198                             tag : 'h3',
25199                             cls : 'roo-headline',
25200                             html : this.headline
25201                         },
25202                         {
25203                             tag : 'p',
25204                             cls : 'roo-content',
25205                             html : this.content
25206                         }
25207                     ]
25208                 }
25209             ]
25210         };
25211         
25212         if(this.icon){
25213             cfg.cn.push({
25214                 tag : 'div',
25215                 cls : 'icon',
25216                 cn :[
25217                     {
25218                         tag : 'i',
25219                         cls : 'ion ' + this.icon
25220                     }
25221                 ]
25222             });
25223         }
25224         
25225         if(this.footer){
25226             var footer = {
25227                 tag : 'a',
25228                 cls : 'small-box-footer',
25229                 href : this.fhref || '#',
25230                 html : this.footer
25231             };
25232             
25233             cfg.cn.push(footer);
25234             
25235         }
25236         
25237         return  cfg;
25238     },
25239
25240     onRender : function(ct,position){
25241         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25242
25243
25244        
25245                 
25246     },
25247
25248     setHeadline: function (value)
25249     {
25250         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25251     },
25252     
25253     setFooter: function (value, href)
25254     {
25255         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25256         
25257         if(href){
25258             this.el.select('a.small-box-footer',true).first().attr('href', href);
25259         }
25260         
25261     },
25262
25263     setContent: function (value)
25264     {
25265         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25266     },
25267
25268     initEvents: function() 
25269     {   
25270         
25271     }
25272     
25273 });
25274
25275  
25276 /*
25277  * - LGPL
25278  *
25279  * TabBox
25280  * 
25281  */
25282 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25283
25284 /**
25285  * @class Roo.bootstrap.dash.TabBox
25286  * @extends Roo.bootstrap.Component
25287  * Bootstrap TabBox class
25288  * @cfg {String} title Title of the TabBox
25289  * @cfg {String} icon Icon of the TabBox
25290  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25291  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25292  * 
25293  * @constructor
25294  * Create a new TabBox
25295  * @param {Object} config The config object
25296  */
25297
25298
25299 Roo.bootstrap.dash.TabBox = function(config){
25300     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25301     this.addEvents({
25302         // raw events
25303         /**
25304          * @event addpane
25305          * When a pane is added
25306          * @param {Roo.bootstrap.dash.TabPane} pane
25307          */
25308         "addpane" : true,
25309         /**
25310          * @event activatepane
25311          * When a pane is activated
25312          * @param {Roo.bootstrap.dash.TabPane} pane
25313          */
25314         "activatepane" : true
25315         
25316          
25317     });
25318     
25319     this.panes = [];
25320 };
25321
25322 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25323
25324     title : '',
25325     icon : false,
25326     showtabs : true,
25327     tabScrollable : false,
25328     
25329     getChildContainer : function()
25330     {
25331         return this.el.select('.tab-content', true).first();
25332     },
25333     
25334     getAutoCreate : function(){
25335         
25336         var header = {
25337             tag: 'li',
25338             cls: 'pull-left header',
25339             html: this.title,
25340             cn : []
25341         };
25342         
25343         if(this.icon){
25344             header.cn.push({
25345                 tag: 'i',
25346                 cls: 'fa ' + this.icon
25347             });
25348         }
25349         
25350         var h = {
25351             tag: 'ul',
25352             cls: 'nav nav-tabs pull-right',
25353             cn: [
25354                 header
25355             ]
25356         };
25357         
25358         if(this.tabScrollable){
25359             h = {
25360                 tag: 'div',
25361                 cls: 'tab-header',
25362                 cn: [
25363                     {
25364                         tag: 'ul',
25365                         cls: 'nav nav-tabs pull-right',
25366                         cn: [
25367                             header
25368                         ]
25369                     }
25370                 ]
25371             };
25372         }
25373         
25374         var cfg = {
25375             tag: 'div',
25376             cls: 'nav-tabs-custom',
25377             cn: [
25378                 h,
25379                 {
25380                     tag: 'div',
25381                     cls: 'tab-content no-padding',
25382                     cn: []
25383                 }
25384             ]
25385         };
25386
25387         return  cfg;
25388     },
25389     initEvents : function()
25390     {
25391         //Roo.log('add add pane handler');
25392         this.on('addpane', this.onAddPane, this);
25393     },
25394      /**
25395      * Updates the box title
25396      * @param {String} html to set the title to.
25397      */
25398     setTitle : function(value)
25399     {
25400         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25401     },
25402     onAddPane : function(pane)
25403     {
25404         this.panes.push(pane);
25405         //Roo.log('addpane');
25406         //Roo.log(pane);
25407         // tabs are rendere left to right..
25408         if(!this.showtabs){
25409             return;
25410         }
25411         
25412         var ctr = this.el.select('.nav-tabs', true).first();
25413          
25414          
25415         var existing = ctr.select('.nav-tab',true);
25416         var qty = existing.getCount();;
25417         
25418         
25419         var tab = ctr.createChild({
25420             tag : 'li',
25421             cls : 'nav-tab' + (qty ? '' : ' active'),
25422             cn : [
25423                 {
25424                     tag : 'a',
25425                     href:'#',
25426                     html : pane.title
25427                 }
25428             ]
25429         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25430         pane.tab = tab;
25431         
25432         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25433         if (!qty) {
25434             pane.el.addClass('active');
25435         }
25436         
25437                 
25438     },
25439     onTabClick : function(ev,un,ob,pane)
25440     {
25441         //Roo.log('tab - prev default');
25442         ev.preventDefault();
25443         
25444         
25445         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25446         pane.tab.addClass('active');
25447         //Roo.log(pane.title);
25448         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25449         // technically we should have a deactivate event.. but maybe add later.
25450         // and it should not de-activate the selected tab...
25451         this.fireEvent('activatepane', pane);
25452         pane.el.addClass('active');
25453         pane.fireEvent('activate');
25454         
25455         
25456     },
25457     
25458     getActivePane : function()
25459     {
25460         var r = false;
25461         Roo.each(this.panes, function(p) {
25462             if(p.el.hasClass('active')){
25463                 r = p;
25464                 return false;
25465             }
25466             
25467             return;
25468         });
25469         
25470         return r;
25471     }
25472     
25473     
25474 });
25475
25476  
25477 /*
25478  * - LGPL
25479  *
25480  * Tab pane
25481  * 
25482  */
25483 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25484 /**
25485  * @class Roo.bootstrap.TabPane
25486  * @extends Roo.bootstrap.Component
25487  * Bootstrap TabPane class
25488  * @cfg {Boolean} active (false | true) Default false
25489  * @cfg {String} title title of panel
25490
25491  * 
25492  * @constructor
25493  * Create a new TabPane
25494  * @param {Object} config The config object
25495  */
25496
25497 Roo.bootstrap.dash.TabPane = function(config){
25498     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25499     
25500     this.addEvents({
25501         // raw events
25502         /**
25503          * @event activate
25504          * When a pane is activated
25505          * @param {Roo.bootstrap.dash.TabPane} pane
25506          */
25507         "activate" : true
25508          
25509     });
25510 };
25511
25512 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25513     
25514     active : false,
25515     title : '',
25516     
25517     // the tabBox that this is attached to.
25518     tab : false,
25519      
25520     getAutoCreate : function() 
25521     {
25522         var cfg = {
25523             tag: 'div',
25524             cls: 'tab-pane'
25525         };
25526         
25527         if(this.active){
25528             cfg.cls += ' active';
25529         }
25530         
25531         return cfg;
25532     },
25533     initEvents  : function()
25534     {
25535         //Roo.log('trigger add pane handler');
25536         this.parent().fireEvent('addpane', this)
25537     },
25538     
25539      /**
25540      * Updates the tab title 
25541      * @param {String} html to set the title to.
25542      */
25543     setTitle: function(str)
25544     {
25545         if (!this.tab) {
25546             return;
25547         }
25548         this.title = str;
25549         this.tab.select('a', true).first().dom.innerHTML = str;
25550         
25551     }
25552     
25553     
25554     
25555 });
25556
25557  
25558
25559
25560  /*
25561  * - LGPL
25562  *
25563  * menu
25564  * 
25565  */
25566 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25567
25568 /**
25569  * @class Roo.bootstrap.menu.Menu
25570  * @extends Roo.bootstrap.Component
25571  * Bootstrap Menu class - container for Menu
25572  * @cfg {String} html Text of the menu
25573  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25574  * @cfg {String} icon Font awesome icon
25575  * @cfg {String} pos Menu align to (top | bottom) default bottom
25576  * 
25577  * 
25578  * @constructor
25579  * Create a new Menu
25580  * @param {Object} config The config object
25581  */
25582
25583
25584 Roo.bootstrap.menu.Menu = function(config){
25585     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25586     
25587     this.addEvents({
25588         /**
25589          * @event beforeshow
25590          * Fires before this menu is displayed
25591          * @param {Roo.bootstrap.menu.Menu} this
25592          */
25593         beforeshow : true,
25594         /**
25595          * @event beforehide
25596          * Fires before this menu is hidden
25597          * @param {Roo.bootstrap.menu.Menu} this
25598          */
25599         beforehide : true,
25600         /**
25601          * @event show
25602          * Fires after this menu is displayed
25603          * @param {Roo.bootstrap.menu.Menu} this
25604          */
25605         show : true,
25606         /**
25607          * @event hide
25608          * Fires after this menu is hidden
25609          * @param {Roo.bootstrap.menu.Menu} this
25610          */
25611         hide : true,
25612         /**
25613          * @event click
25614          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25615          * @param {Roo.bootstrap.menu.Menu} this
25616          * @param {Roo.EventObject} e
25617          */
25618         click : true
25619     });
25620     
25621 };
25622
25623 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25624     
25625     submenu : false,
25626     html : '',
25627     weight : 'default',
25628     icon : false,
25629     pos : 'bottom',
25630     
25631     
25632     getChildContainer : function() {
25633         if(this.isSubMenu){
25634             return this.el;
25635         }
25636         
25637         return this.el.select('ul.dropdown-menu', true).first();  
25638     },
25639     
25640     getAutoCreate : function()
25641     {
25642         var text = [
25643             {
25644                 tag : 'span',
25645                 cls : 'roo-menu-text',
25646                 html : this.html
25647             }
25648         ];
25649         
25650         if(this.icon){
25651             text.unshift({
25652                 tag : 'i',
25653                 cls : 'fa ' + this.icon
25654             })
25655         }
25656         
25657         
25658         var cfg = {
25659             tag : 'div',
25660             cls : 'btn-group',
25661             cn : [
25662                 {
25663                     tag : 'button',
25664                     cls : 'dropdown-button btn btn-' + this.weight,
25665                     cn : text
25666                 },
25667                 {
25668                     tag : 'button',
25669                     cls : 'dropdown-toggle btn btn-' + this.weight,
25670                     cn : [
25671                         {
25672                             tag : 'span',
25673                             cls : 'caret'
25674                         }
25675                     ]
25676                 },
25677                 {
25678                     tag : 'ul',
25679                     cls : 'dropdown-menu'
25680                 }
25681             ]
25682             
25683         };
25684         
25685         if(this.pos == 'top'){
25686             cfg.cls += ' dropup';
25687         }
25688         
25689         if(this.isSubMenu){
25690             cfg = {
25691                 tag : 'ul',
25692                 cls : 'dropdown-menu'
25693             }
25694         }
25695         
25696         return cfg;
25697     },
25698     
25699     onRender : function(ct, position)
25700     {
25701         this.isSubMenu = ct.hasClass('dropdown-submenu');
25702         
25703         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25704     },
25705     
25706     initEvents : function() 
25707     {
25708         if(this.isSubMenu){
25709             return;
25710         }
25711         
25712         this.hidden = true;
25713         
25714         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25715         this.triggerEl.on('click', this.onTriggerPress, this);
25716         
25717         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25718         this.buttonEl.on('click', this.onClick, this);
25719         
25720     },
25721     
25722     list : function()
25723     {
25724         if(this.isSubMenu){
25725             return this.el;
25726         }
25727         
25728         return this.el.select('ul.dropdown-menu', true).first();
25729     },
25730     
25731     onClick : function(e)
25732     {
25733         this.fireEvent("click", this, e);
25734     },
25735     
25736     onTriggerPress  : function(e)
25737     {   
25738         if (this.isVisible()) {
25739             this.hide();
25740         } else {
25741             this.show();
25742         }
25743     },
25744     
25745     isVisible : function(){
25746         return !this.hidden;
25747     },
25748     
25749     show : function()
25750     {
25751         this.fireEvent("beforeshow", this);
25752         
25753         this.hidden = false;
25754         this.el.addClass('open');
25755         
25756         Roo.get(document).on("mouseup", this.onMouseUp, this);
25757         
25758         this.fireEvent("show", this);
25759         
25760         
25761     },
25762     
25763     hide : function()
25764     {
25765         this.fireEvent("beforehide", this);
25766         
25767         this.hidden = true;
25768         this.el.removeClass('open');
25769         
25770         Roo.get(document).un("mouseup", this.onMouseUp);
25771         
25772         this.fireEvent("hide", this);
25773     },
25774     
25775     onMouseUp : function()
25776     {
25777         this.hide();
25778     }
25779     
25780 });
25781
25782  
25783  /*
25784  * - LGPL
25785  *
25786  * menu item
25787  * 
25788  */
25789 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25790
25791 /**
25792  * @class Roo.bootstrap.menu.Item
25793  * @extends Roo.bootstrap.Component
25794  * Bootstrap MenuItem class
25795  * @cfg {Boolean} submenu (true | false) default false
25796  * @cfg {String} html text of the item
25797  * @cfg {String} href the link
25798  * @cfg {Boolean} disable (true | false) default false
25799  * @cfg {Boolean} preventDefault (true | false) default true
25800  * @cfg {String} icon Font awesome icon
25801  * @cfg {String} pos Submenu align to (left | right) default right 
25802  * 
25803  * 
25804  * @constructor
25805  * Create a new Item
25806  * @param {Object} config The config object
25807  */
25808
25809
25810 Roo.bootstrap.menu.Item = function(config){
25811     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25812     this.addEvents({
25813         /**
25814          * @event mouseover
25815          * Fires when the mouse is hovering over this menu
25816          * @param {Roo.bootstrap.menu.Item} this
25817          * @param {Roo.EventObject} e
25818          */
25819         mouseover : true,
25820         /**
25821          * @event mouseout
25822          * Fires when the mouse exits this menu
25823          * @param {Roo.bootstrap.menu.Item} this
25824          * @param {Roo.EventObject} e
25825          */
25826         mouseout : true,
25827         // raw events
25828         /**
25829          * @event click
25830          * The raw click event for the entire grid.
25831          * @param {Roo.EventObject} e
25832          */
25833         click : true
25834     });
25835 };
25836
25837 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25838     
25839     submenu : false,
25840     href : '',
25841     html : '',
25842     preventDefault: true,
25843     disable : false,
25844     icon : false,
25845     pos : 'right',
25846     
25847     getAutoCreate : function()
25848     {
25849         var text = [
25850             {
25851                 tag : 'span',
25852                 cls : 'roo-menu-item-text',
25853                 html : this.html
25854             }
25855         ];
25856         
25857         if(this.icon){
25858             text.unshift({
25859                 tag : 'i',
25860                 cls : 'fa ' + this.icon
25861             })
25862         }
25863         
25864         var cfg = {
25865             tag : 'li',
25866             cn : [
25867                 {
25868                     tag : 'a',
25869                     href : this.href || '#',
25870                     cn : text
25871                 }
25872             ]
25873         };
25874         
25875         if(this.disable){
25876             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25877         }
25878         
25879         if(this.submenu){
25880             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25881             
25882             if(this.pos == 'left'){
25883                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25884             }
25885         }
25886         
25887         return cfg;
25888     },
25889     
25890     initEvents : function() 
25891     {
25892         this.el.on('mouseover', this.onMouseOver, this);
25893         this.el.on('mouseout', this.onMouseOut, this);
25894         
25895         this.el.select('a', true).first().on('click', this.onClick, this);
25896         
25897     },
25898     
25899     onClick : function(e)
25900     {
25901         if(this.preventDefault){
25902             e.preventDefault();
25903         }
25904         
25905         this.fireEvent("click", this, e);
25906     },
25907     
25908     onMouseOver : function(e)
25909     {
25910         if(this.submenu && this.pos == 'left'){
25911             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25912         }
25913         
25914         this.fireEvent("mouseover", this, e);
25915     },
25916     
25917     onMouseOut : function(e)
25918     {
25919         this.fireEvent("mouseout", this, e);
25920     }
25921 });
25922
25923  
25924
25925  /*
25926  * - LGPL
25927  *
25928  * menu separator
25929  * 
25930  */
25931 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25932
25933 /**
25934  * @class Roo.bootstrap.menu.Separator
25935  * @extends Roo.bootstrap.Component
25936  * Bootstrap Separator class
25937  * 
25938  * @constructor
25939  * Create a new Separator
25940  * @param {Object} config The config object
25941  */
25942
25943
25944 Roo.bootstrap.menu.Separator = function(config){
25945     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25946 };
25947
25948 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25949     
25950     getAutoCreate : function(){
25951         var cfg = {
25952             tag : 'li',
25953             cls: 'divider'
25954         };
25955         
25956         return cfg;
25957     }
25958    
25959 });
25960
25961  
25962
25963  /*
25964  * - LGPL
25965  *
25966  * Tooltip
25967  * 
25968  */
25969
25970 /**
25971  * @class Roo.bootstrap.Tooltip
25972  * Bootstrap Tooltip class
25973  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25974  * to determine which dom element triggers the tooltip.
25975  * 
25976  * It needs to add support for additional attributes like tooltip-position
25977  * 
25978  * @constructor
25979  * Create a new Toolti
25980  * @param {Object} config The config object
25981  */
25982
25983 Roo.bootstrap.Tooltip = function(config){
25984     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25985     
25986     this.alignment = Roo.bootstrap.Tooltip.alignment;
25987     
25988     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25989         this.alignment = config.alignment;
25990     }
25991     
25992 };
25993
25994 Roo.apply(Roo.bootstrap.Tooltip, {
25995     /**
25996      * @function init initialize tooltip monitoring.
25997      * @static
25998      */
25999     currentEl : false,
26000     currentTip : false,
26001     currentRegion : false,
26002     
26003     //  init : delay?
26004     
26005     init : function()
26006     {
26007         Roo.get(document).on('mouseover', this.enter ,this);
26008         Roo.get(document).on('mouseout', this.leave, this);
26009          
26010         
26011         this.currentTip = new Roo.bootstrap.Tooltip();
26012     },
26013     
26014     enter : function(ev)
26015     {
26016         var dom = ev.getTarget();
26017         
26018         //Roo.log(['enter',dom]);
26019         var el = Roo.fly(dom);
26020         if (this.currentEl) {
26021             //Roo.log(dom);
26022             //Roo.log(this.currentEl);
26023             //Roo.log(this.currentEl.contains(dom));
26024             if (this.currentEl == el) {
26025                 return;
26026             }
26027             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26028                 return;
26029             }
26030
26031         }
26032         
26033         if (this.currentTip.el) {
26034             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26035         }    
26036         //Roo.log(ev);
26037         
26038         if(!el || el.dom == document){
26039             return;
26040         }
26041         
26042         var bindEl = el;
26043         
26044         // you can not look for children, as if el is the body.. then everythign is the child..
26045         if (!el.attr('tooltip')) { //
26046             if (!el.select("[tooltip]").elements.length) {
26047                 return;
26048             }
26049             // is the mouse over this child...?
26050             bindEl = el.select("[tooltip]").first();
26051             var xy = ev.getXY();
26052             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26053                 //Roo.log("not in region.");
26054                 return;
26055             }
26056             //Roo.log("child element over..");
26057             
26058         }
26059         this.currentEl = bindEl;
26060         this.currentTip.bind(bindEl);
26061         this.currentRegion = Roo.lib.Region.getRegion(dom);
26062         this.currentTip.enter();
26063         
26064     },
26065     leave : function(ev)
26066     {
26067         var dom = ev.getTarget();
26068         //Roo.log(['leave',dom]);
26069         if (!this.currentEl) {
26070             return;
26071         }
26072         
26073         
26074         if (dom != this.currentEl.dom) {
26075             return;
26076         }
26077         var xy = ev.getXY();
26078         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26079             return;
26080         }
26081         // only activate leave if mouse cursor is outside... bounding box..
26082         
26083         
26084         
26085         
26086         if (this.currentTip) {
26087             this.currentTip.leave();
26088         }
26089         //Roo.log('clear currentEl');
26090         this.currentEl = false;
26091         
26092         
26093     },
26094     alignment : {
26095         'left' : ['r-l', [-2,0], 'right'],
26096         'right' : ['l-r', [2,0], 'left'],
26097         'bottom' : ['t-b', [0,2], 'top'],
26098         'top' : [ 'b-t', [0,-2], 'bottom']
26099     }
26100     
26101 });
26102
26103
26104 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26105     
26106     
26107     bindEl : false,
26108     
26109     delay : null, // can be { show : 300 , hide: 500}
26110     
26111     timeout : null,
26112     
26113     hoverState : null, //???
26114     
26115     placement : 'bottom', 
26116     
26117     alignment : false,
26118     
26119     getAutoCreate : function(){
26120     
26121         var cfg = {
26122            cls : 'tooltip',
26123            role : 'tooltip',
26124            cn : [
26125                 {
26126                     cls : 'tooltip-arrow'
26127                 },
26128                 {
26129                     cls : 'tooltip-inner'
26130                 }
26131            ]
26132         };
26133         
26134         return cfg;
26135     },
26136     bind : function(el)
26137     {
26138         this.bindEl = el;
26139     },
26140       
26141     
26142     enter : function () {
26143        
26144         if (this.timeout != null) {
26145             clearTimeout(this.timeout);
26146         }
26147         
26148         this.hoverState = 'in';
26149          //Roo.log("enter - show");
26150         if (!this.delay || !this.delay.show) {
26151             this.show();
26152             return;
26153         }
26154         var _t = this;
26155         this.timeout = setTimeout(function () {
26156             if (_t.hoverState == 'in') {
26157                 _t.show();
26158             }
26159         }, this.delay.show);
26160     },
26161     leave : function()
26162     {
26163         clearTimeout(this.timeout);
26164     
26165         this.hoverState = 'out';
26166          if (!this.delay || !this.delay.hide) {
26167             this.hide();
26168             return;
26169         }
26170        
26171         var _t = this;
26172         this.timeout = setTimeout(function () {
26173             //Roo.log("leave - timeout");
26174             
26175             if (_t.hoverState == 'out') {
26176                 _t.hide();
26177                 Roo.bootstrap.Tooltip.currentEl = false;
26178             }
26179         }, delay);
26180     },
26181     
26182     show : function (msg)
26183     {
26184         if (!this.el) {
26185             this.render(document.body);
26186         }
26187         // set content.
26188         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26189         
26190         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26191         
26192         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26193         
26194         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26195         
26196         var placement = typeof this.placement == 'function' ?
26197             this.placement.call(this, this.el, on_el) :
26198             this.placement;
26199             
26200         var autoToken = /\s?auto?\s?/i;
26201         var autoPlace = autoToken.test(placement);
26202         if (autoPlace) {
26203             placement = placement.replace(autoToken, '') || 'top';
26204         }
26205         
26206         //this.el.detach()
26207         //this.el.setXY([0,0]);
26208         this.el.show();
26209         //this.el.dom.style.display='block';
26210         
26211         //this.el.appendTo(on_el);
26212         
26213         var p = this.getPosition();
26214         var box = this.el.getBox();
26215         
26216         if (autoPlace) {
26217             // fixme..
26218         }
26219         
26220         var align = this.alignment[placement];
26221         
26222         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26223         
26224         if(placement == 'top' || placement == 'bottom'){
26225             if(xy[0] < 0){
26226                 placement = 'right';
26227             }
26228             
26229             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26230                 placement = 'left';
26231             }
26232             
26233             var scroll = Roo.select('body', true).first().getScroll();
26234             
26235             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26236                 placement = 'top';
26237             }
26238             
26239             align = this.alignment[placement];
26240         }
26241         
26242         this.el.alignTo(this.bindEl, align[0],align[1]);
26243         //var arrow = this.el.select('.arrow',true).first();
26244         //arrow.set(align[2], 
26245         
26246         this.el.addClass(placement);
26247         
26248         this.el.addClass('in fade');
26249         
26250         this.hoverState = null;
26251         
26252         if (this.el.hasClass('fade')) {
26253             // fade it?
26254         }
26255         
26256     },
26257     hide : function()
26258     {
26259          
26260         if (!this.el) {
26261             return;
26262         }
26263         //this.el.setXY([0,0]);
26264         this.el.removeClass('in');
26265         //this.el.hide();
26266         
26267     }
26268     
26269 });
26270  
26271
26272  /*
26273  * - LGPL
26274  *
26275  * Location Picker
26276  * 
26277  */
26278
26279 /**
26280  * @class Roo.bootstrap.LocationPicker
26281  * @extends Roo.bootstrap.Component
26282  * Bootstrap LocationPicker class
26283  * @cfg {Number} latitude Position when init default 0
26284  * @cfg {Number} longitude Position when init default 0
26285  * @cfg {Number} zoom default 15
26286  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26287  * @cfg {Boolean} mapTypeControl default false
26288  * @cfg {Boolean} disableDoubleClickZoom default false
26289  * @cfg {Boolean} scrollwheel default true
26290  * @cfg {Boolean} streetViewControl default false
26291  * @cfg {Number} radius default 0
26292  * @cfg {String} locationName
26293  * @cfg {Boolean} draggable default true
26294  * @cfg {Boolean} enableAutocomplete default false
26295  * @cfg {Boolean} enableReverseGeocode default true
26296  * @cfg {String} markerTitle
26297  * 
26298  * @constructor
26299  * Create a new LocationPicker
26300  * @param {Object} config The config object
26301  */
26302
26303
26304 Roo.bootstrap.LocationPicker = function(config){
26305     
26306     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26307     
26308     this.addEvents({
26309         /**
26310          * @event initial
26311          * Fires when the picker initialized.
26312          * @param {Roo.bootstrap.LocationPicker} this
26313          * @param {Google Location} location
26314          */
26315         initial : true,
26316         /**
26317          * @event positionchanged
26318          * Fires when the picker position changed.
26319          * @param {Roo.bootstrap.LocationPicker} this
26320          * @param {Google Location} location
26321          */
26322         positionchanged : true,
26323         /**
26324          * @event resize
26325          * Fires when the map resize.
26326          * @param {Roo.bootstrap.LocationPicker} this
26327          */
26328         resize : true,
26329         /**
26330          * @event show
26331          * Fires when the map show.
26332          * @param {Roo.bootstrap.LocationPicker} this
26333          */
26334         show : true,
26335         /**
26336          * @event hide
26337          * Fires when the map hide.
26338          * @param {Roo.bootstrap.LocationPicker} this
26339          */
26340         hide : true,
26341         /**
26342          * @event mapClick
26343          * Fires when click the map.
26344          * @param {Roo.bootstrap.LocationPicker} this
26345          * @param {Map event} e
26346          */
26347         mapClick : true,
26348         /**
26349          * @event mapRightClick
26350          * Fires when right click the map.
26351          * @param {Roo.bootstrap.LocationPicker} this
26352          * @param {Map event} e
26353          */
26354         mapRightClick : true,
26355         /**
26356          * @event markerClick
26357          * Fires when click the marker.
26358          * @param {Roo.bootstrap.LocationPicker} this
26359          * @param {Map event} e
26360          */
26361         markerClick : true,
26362         /**
26363          * @event markerRightClick
26364          * Fires when right click the marker.
26365          * @param {Roo.bootstrap.LocationPicker} this
26366          * @param {Map event} e
26367          */
26368         markerRightClick : true,
26369         /**
26370          * @event OverlayViewDraw
26371          * Fires when OverlayView Draw
26372          * @param {Roo.bootstrap.LocationPicker} this
26373          */
26374         OverlayViewDraw : true,
26375         /**
26376          * @event OverlayViewOnAdd
26377          * Fires when OverlayView Draw
26378          * @param {Roo.bootstrap.LocationPicker} this
26379          */
26380         OverlayViewOnAdd : true,
26381         /**
26382          * @event OverlayViewOnRemove
26383          * Fires when OverlayView Draw
26384          * @param {Roo.bootstrap.LocationPicker} this
26385          */
26386         OverlayViewOnRemove : true,
26387         /**
26388          * @event OverlayViewShow
26389          * Fires when OverlayView Draw
26390          * @param {Roo.bootstrap.LocationPicker} this
26391          * @param {Pixel} cpx
26392          */
26393         OverlayViewShow : true,
26394         /**
26395          * @event OverlayViewHide
26396          * Fires when OverlayView Draw
26397          * @param {Roo.bootstrap.LocationPicker} this
26398          */
26399         OverlayViewHide : true,
26400         /**
26401          * @event loadexception
26402          * Fires when load google lib failed.
26403          * @param {Roo.bootstrap.LocationPicker} this
26404          */
26405         loadexception : true
26406     });
26407         
26408 };
26409
26410 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26411     
26412     gMapContext: false,
26413     
26414     latitude: 0,
26415     longitude: 0,
26416     zoom: 15,
26417     mapTypeId: false,
26418     mapTypeControl: false,
26419     disableDoubleClickZoom: false,
26420     scrollwheel: true,
26421     streetViewControl: false,
26422     radius: 0,
26423     locationName: '',
26424     draggable: true,
26425     enableAutocomplete: false,
26426     enableReverseGeocode: true,
26427     markerTitle: '',
26428     
26429     getAutoCreate: function()
26430     {
26431
26432         var cfg = {
26433             tag: 'div',
26434             cls: 'roo-location-picker'
26435         };
26436         
26437         return cfg
26438     },
26439     
26440     initEvents: function(ct, position)
26441     {       
26442         if(!this.el.getWidth() || this.isApplied()){
26443             return;
26444         }
26445         
26446         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26447         
26448         this.initial();
26449     },
26450     
26451     initial: function()
26452     {
26453         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26454             this.fireEvent('loadexception', this);
26455             return;
26456         }
26457         
26458         if(!this.mapTypeId){
26459             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26460         }
26461         
26462         this.gMapContext = this.GMapContext();
26463         
26464         this.initOverlayView();
26465         
26466         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26467         
26468         var _this = this;
26469                 
26470         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26471             _this.setPosition(_this.gMapContext.marker.position);
26472         });
26473         
26474         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26475             _this.fireEvent('mapClick', this, event);
26476             
26477         });
26478
26479         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26480             _this.fireEvent('mapRightClick', this, event);
26481             
26482         });
26483         
26484         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26485             _this.fireEvent('markerClick', this, event);
26486             
26487         });
26488
26489         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26490             _this.fireEvent('markerRightClick', this, event);
26491             
26492         });
26493         
26494         this.setPosition(this.gMapContext.location);
26495         
26496         this.fireEvent('initial', this, this.gMapContext.location);
26497     },
26498     
26499     initOverlayView: function()
26500     {
26501         var _this = this;
26502         
26503         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26504             
26505             draw: function()
26506             {
26507                 _this.fireEvent('OverlayViewDraw', _this);
26508             },
26509             
26510             onAdd: function()
26511             {
26512                 _this.fireEvent('OverlayViewOnAdd', _this);
26513             },
26514             
26515             onRemove: function()
26516             {
26517                 _this.fireEvent('OverlayViewOnRemove', _this);
26518             },
26519             
26520             show: function(cpx)
26521             {
26522                 _this.fireEvent('OverlayViewShow', _this, cpx);
26523             },
26524             
26525             hide: function()
26526             {
26527                 _this.fireEvent('OverlayViewHide', _this);
26528             }
26529             
26530         });
26531     },
26532     
26533     fromLatLngToContainerPixel: function(event)
26534     {
26535         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26536     },
26537     
26538     isApplied: function() 
26539     {
26540         return this.getGmapContext() == false ? false : true;
26541     },
26542     
26543     getGmapContext: function() 
26544     {
26545         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26546     },
26547     
26548     GMapContext: function() 
26549     {
26550         var position = new google.maps.LatLng(this.latitude, this.longitude);
26551         
26552         var _map = new google.maps.Map(this.el.dom, {
26553             center: position,
26554             zoom: this.zoom,
26555             mapTypeId: this.mapTypeId,
26556             mapTypeControl: this.mapTypeControl,
26557             disableDoubleClickZoom: this.disableDoubleClickZoom,
26558             scrollwheel: this.scrollwheel,
26559             streetViewControl: this.streetViewControl,
26560             locationName: this.locationName,
26561             draggable: this.draggable,
26562             enableAutocomplete: this.enableAutocomplete,
26563             enableReverseGeocode: this.enableReverseGeocode
26564         });
26565         
26566         var _marker = new google.maps.Marker({
26567             position: position,
26568             map: _map,
26569             title: this.markerTitle,
26570             draggable: this.draggable
26571         });
26572         
26573         return {
26574             map: _map,
26575             marker: _marker,
26576             circle: null,
26577             location: position,
26578             radius: this.radius,
26579             locationName: this.locationName,
26580             addressComponents: {
26581                 formatted_address: null,
26582                 addressLine1: null,
26583                 addressLine2: null,
26584                 streetName: null,
26585                 streetNumber: null,
26586                 city: null,
26587                 district: null,
26588                 state: null,
26589                 stateOrProvince: null
26590             },
26591             settings: this,
26592             domContainer: this.el.dom,
26593             geodecoder: new google.maps.Geocoder()
26594         };
26595     },
26596     
26597     drawCircle: function(center, radius, options) 
26598     {
26599         if (this.gMapContext.circle != null) {
26600             this.gMapContext.circle.setMap(null);
26601         }
26602         if (radius > 0) {
26603             radius *= 1;
26604             options = Roo.apply({}, options, {
26605                 strokeColor: "#0000FF",
26606                 strokeOpacity: .35,
26607                 strokeWeight: 2,
26608                 fillColor: "#0000FF",
26609                 fillOpacity: .2
26610             });
26611             
26612             options.map = this.gMapContext.map;
26613             options.radius = radius;
26614             options.center = center;
26615             this.gMapContext.circle = new google.maps.Circle(options);
26616             return this.gMapContext.circle;
26617         }
26618         
26619         return null;
26620     },
26621     
26622     setPosition: function(location) 
26623     {
26624         this.gMapContext.location = location;
26625         this.gMapContext.marker.setPosition(location);
26626         this.gMapContext.map.panTo(location);
26627         this.drawCircle(location, this.gMapContext.radius, {});
26628         
26629         var _this = this;
26630         
26631         if (this.gMapContext.settings.enableReverseGeocode) {
26632             this.gMapContext.geodecoder.geocode({
26633                 latLng: this.gMapContext.location
26634             }, function(results, status) {
26635                 
26636                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26637                     _this.gMapContext.locationName = results[0].formatted_address;
26638                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26639                     
26640                     _this.fireEvent('positionchanged', this, location);
26641                 }
26642             });
26643             
26644             return;
26645         }
26646         
26647         this.fireEvent('positionchanged', this, location);
26648     },
26649     
26650     resize: function()
26651     {
26652         google.maps.event.trigger(this.gMapContext.map, "resize");
26653         
26654         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26655         
26656         this.fireEvent('resize', this);
26657     },
26658     
26659     setPositionByLatLng: function(latitude, longitude)
26660     {
26661         this.setPosition(new google.maps.LatLng(latitude, longitude));
26662     },
26663     
26664     getCurrentPosition: function() 
26665     {
26666         return {
26667             latitude: this.gMapContext.location.lat(),
26668             longitude: this.gMapContext.location.lng()
26669         };
26670     },
26671     
26672     getAddressName: function() 
26673     {
26674         return this.gMapContext.locationName;
26675     },
26676     
26677     getAddressComponents: function() 
26678     {
26679         return this.gMapContext.addressComponents;
26680     },
26681     
26682     address_component_from_google_geocode: function(address_components) 
26683     {
26684         var result = {};
26685         
26686         for (var i = 0; i < address_components.length; i++) {
26687             var component = address_components[i];
26688             if (component.types.indexOf("postal_code") >= 0) {
26689                 result.postalCode = component.short_name;
26690             } else if (component.types.indexOf("street_number") >= 0) {
26691                 result.streetNumber = component.short_name;
26692             } else if (component.types.indexOf("route") >= 0) {
26693                 result.streetName = component.short_name;
26694             } else if (component.types.indexOf("neighborhood") >= 0) {
26695                 result.city = component.short_name;
26696             } else if (component.types.indexOf("locality") >= 0) {
26697                 result.city = component.short_name;
26698             } else if (component.types.indexOf("sublocality") >= 0) {
26699                 result.district = component.short_name;
26700             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26701                 result.stateOrProvince = component.short_name;
26702             } else if (component.types.indexOf("country") >= 0) {
26703                 result.country = component.short_name;
26704             }
26705         }
26706         
26707         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26708         result.addressLine2 = "";
26709         return result;
26710     },
26711     
26712     setZoomLevel: function(zoom)
26713     {
26714         this.gMapContext.map.setZoom(zoom);
26715     },
26716     
26717     show: function()
26718     {
26719         if(!this.el){
26720             return;
26721         }
26722         
26723         this.el.show();
26724         
26725         this.resize();
26726         
26727         this.fireEvent('show', this);
26728     },
26729     
26730     hide: function()
26731     {
26732         if(!this.el){
26733             return;
26734         }
26735         
26736         this.el.hide();
26737         
26738         this.fireEvent('hide', this);
26739     }
26740     
26741 });
26742
26743 Roo.apply(Roo.bootstrap.LocationPicker, {
26744     
26745     OverlayView : function(map, options)
26746     {
26747         options = options || {};
26748         
26749         this.setMap(map);
26750     }
26751     
26752     
26753 });/*
26754  * - LGPL
26755  *
26756  * Alert
26757  * 
26758  */
26759
26760 /**
26761  * @class Roo.bootstrap.Alert
26762  * @extends Roo.bootstrap.Component
26763  * Bootstrap Alert class
26764  * @cfg {String} title The title of alert
26765  * @cfg {String} html The content of alert
26766  * @cfg {String} weight (  success | info | warning | danger )
26767  * @cfg {String} faicon font-awesomeicon
26768  * 
26769  * @constructor
26770  * Create a new alert
26771  * @param {Object} config The config object
26772  */
26773
26774
26775 Roo.bootstrap.Alert = function(config){
26776     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26777     
26778 };
26779
26780 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26781     
26782     title: '',
26783     html: '',
26784     weight: false,
26785     faicon: false,
26786     
26787     getAutoCreate : function()
26788     {
26789         
26790         var cfg = {
26791             tag : 'div',
26792             cls : 'alert',
26793             cn : [
26794                 {
26795                     tag : 'i',
26796                     cls : 'roo-alert-icon'
26797                     
26798                 },
26799                 {
26800                     tag : 'b',
26801                     cls : 'roo-alert-title',
26802                     html : this.title
26803                 },
26804                 {
26805                     tag : 'span',
26806                     cls : 'roo-alert-text',
26807                     html : this.html
26808                 }
26809             ]
26810         };
26811         
26812         if(this.faicon){
26813             cfg.cn[0].cls += ' fa ' + this.faicon;
26814         }
26815         
26816         if(this.weight){
26817             cfg.cls += ' alert-' + this.weight;
26818         }
26819         
26820         return cfg;
26821     },
26822     
26823     initEvents: function() 
26824     {
26825         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26826     },
26827     
26828     setTitle : function(str)
26829     {
26830         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26831     },
26832     
26833     setText : function(str)
26834     {
26835         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26836     },
26837     
26838     setWeight : function(weight)
26839     {
26840         if(this.weight){
26841             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26842         }
26843         
26844         this.weight = weight;
26845         
26846         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26847     },
26848     
26849     setIcon : function(icon)
26850     {
26851         if(this.faicon){
26852             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26853         }
26854         
26855         this.faicon = icon;
26856         
26857         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26858     },
26859     
26860     hide: function() 
26861     {
26862         this.el.hide();   
26863     },
26864     
26865     show: function() 
26866     {  
26867         this.el.show();   
26868     }
26869     
26870 });
26871
26872  
26873 /*
26874 * Licence: LGPL
26875 */
26876
26877 /**
26878  * @class Roo.bootstrap.UploadCropbox
26879  * @extends Roo.bootstrap.Component
26880  * Bootstrap UploadCropbox class
26881  * @cfg {String} emptyText show when image has been loaded
26882  * @cfg {String} rotateNotify show when image too small to rotate
26883  * @cfg {Number} errorTimeout default 3000
26884  * @cfg {Number} minWidth default 300
26885  * @cfg {Number} minHeight default 300
26886  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26887  * @cfg {Boolean} isDocument (true|false) default false
26888  * @cfg {String} url action url
26889  * @cfg {String} paramName default 'imageUpload'
26890  * @cfg {String} method default POST
26891  * @cfg {Boolean} loadMask (true|false) default true
26892  * @cfg {Boolean} loadingText default 'Loading...'
26893  * 
26894  * @constructor
26895  * Create a new UploadCropbox
26896  * @param {Object} config The config object
26897  */
26898
26899 Roo.bootstrap.UploadCropbox = function(config){
26900     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26901     
26902     this.addEvents({
26903         /**
26904          * @event beforeselectfile
26905          * Fire before select file
26906          * @param {Roo.bootstrap.UploadCropbox} this
26907          */
26908         "beforeselectfile" : true,
26909         /**
26910          * @event initial
26911          * Fire after initEvent
26912          * @param {Roo.bootstrap.UploadCropbox} this
26913          */
26914         "initial" : true,
26915         /**
26916          * @event crop
26917          * Fire after initEvent
26918          * @param {Roo.bootstrap.UploadCropbox} this
26919          * @param {String} data
26920          */
26921         "crop" : true,
26922         /**
26923          * @event prepare
26924          * Fire when preparing the file data
26925          * @param {Roo.bootstrap.UploadCropbox} this
26926          * @param {Object} file
26927          */
26928         "prepare" : true,
26929         /**
26930          * @event exception
26931          * Fire when get exception
26932          * @param {Roo.bootstrap.UploadCropbox} this
26933          * @param {XMLHttpRequest} xhr
26934          */
26935         "exception" : true,
26936         /**
26937          * @event beforeloadcanvas
26938          * Fire before load the canvas
26939          * @param {Roo.bootstrap.UploadCropbox} this
26940          * @param {String} src
26941          */
26942         "beforeloadcanvas" : true,
26943         /**
26944          * @event trash
26945          * Fire when trash image
26946          * @param {Roo.bootstrap.UploadCropbox} this
26947          */
26948         "trash" : true,
26949         /**
26950          * @event download
26951          * Fire when download the image
26952          * @param {Roo.bootstrap.UploadCropbox} this
26953          */
26954         "download" : true,
26955         /**
26956          * @event footerbuttonclick
26957          * Fire when footerbuttonclick
26958          * @param {Roo.bootstrap.UploadCropbox} this
26959          * @param {String} type
26960          */
26961         "footerbuttonclick" : true,
26962         /**
26963          * @event resize
26964          * Fire when resize
26965          * @param {Roo.bootstrap.UploadCropbox} this
26966          */
26967         "resize" : true,
26968         /**
26969          * @event rotate
26970          * Fire when rotate the image
26971          * @param {Roo.bootstrap.UploadCropbox} this
26972          * @param {String} pos
26973          */
26974         "rotate" : true,
26975         /**
26976          * @event inspect
26977          * Fire when inspect the file
26978          * @param {Roo.bootstrap.UploadCropbox} this
26979          * @param {Object} file
26980          */
26981         "inspect" : true,
26982         /**
26983          * @event upload
26984          * Fire when xhr upload the file
26985          * @param {Roo.bootstrap.UploadCropbox} this
26986          * @param {Object} data
26987          */
26988         "upload" : true,
26989         /**
26990          * @event arrange
26991          * Fire when arrange the file data
26992          * @param {Roo.bootstrap.UploadCropbox} this
26993          * @param {Object} formData
26994          */
26995         "arrange" : true
26996     });
26997     
26998     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26999 };
27000
27001 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27002     
27003     emptyText : 'Click to upload image',
27004     rotateNotify : 'Image is too small to rotate',
27005     errorTimeout : 3000,
27006     scale : 0,
27007     baseScale : 1,
27008     rotate : 0,
27009     dragable : false,
27010     pinching : false,
27011     mouseX : 0,
27012     mouseY : 0,
27013     cropData : false,
27014     minWidth : 300,
27015     minHeight : 300,
27016     file : false,
27017     exif : {},
27018     baseRotate : 1,
27019     cropType : 'image/jpeg',
27020     buttons : false,
27021     canvasLoaded : false,
27022     isDocument : false,
27023     method : 'POST',
27024     paramName : 'imageUpload',
27025     loadMask : true,
27026     loadingText : 'Loading...',
27027     maskEl : false,
27028     
27029     getAutoCreate : function()
27030     {
27031         var cfg = {
27032             tag : 'div',
27033             cls : 'roo-upload-cropbox',
27034             cn : [
27035                 {
27036                     tag : 'input',
27037                     cls : 'roo-upload-cropbox-selector',
27038                     type : 'file'
27039                 },
27040                 {
27041                     tag : 'div',
27042                     cls : 'roo-upload-cropbox-body',
27043                     style : 'cursor:pointer',
27044                     cn : [
27045                         {
27046                             tag : 'div',
27047                             cls : 'roo-upload-cropbox-preview'
27048                         },
27049                         {
27050                             tag : 'div',
27051                             cls : 'roo-upload-cropbox-thumb'
27052                         },
27053                         {
27054                             tag : 'div',
27055                             cls : 'roo-upload-cropbox-empty-notify',
27056                             html : this.emptyText
27057                         },
27058                         {
27059                             tag : 'div',
27060                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27061                             html : this.rotateNotify
27062                         }
27063                     ]
27064                 },
27065                 {
27066                     tag : 'div',
27067                     cls : 'roo-upload-cropbox-footer',
27068                     cn : {
27069                         tag : 'div',
27070                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27071                         cn : []
27072                     }
27073                 }
27074             ]
27075         };
27076         
27077         return cfg;
27078     },
27079     
27080     onRender : function(ct, position)
27081     {
27082         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27083         
27084         if (this.buttons.length) {
27085             
27086             Roo.each(this.buttons, function(bb) {
27087                 
27088                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27089                 
27090                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27091                 
27092             }, this);
27093         }
27094         
27095         if(this.loadMask){
27096             this.maskEl = this.el;
27097         }
27098     },
27099     
27100     initEvents : function()
27101     {
27102         this.urlAPI = (window.createObjectURL && window) || 
27103                                 (window.URL && URL.revokeObjectURL && URL) || 
27104                                 (window.webkitURL && webkitURL);
27105                         
27106         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27107         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27108         
27109         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27110         this.selectorEl.hide();
27111         
27112         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27113         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27114         
27115         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27116         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27117         this.thumbEl.hide();
27118         
27119         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27120         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27121         
27122         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27123         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27124         this.errorEl.hide();
27125         
27126         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27127         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27128         this.footerEl.hide();
27129         
27130         this.setThumbBoxSize();
27131         
27132         this.bind();
27133         
27134         this.resize();
27135         
27136         this.fireEvent('initial', this);
27137     },
27138
27139     bind : function()
27140     {
27141         var _this = this;
27142         
27143         window.addEventListener("resize", function() { _this.resize(); } );
27144         
27145         this.bodyEl.on('click', this.beforeSelectFile, this);
27146         
27147         if(Roo.isTouch){
27148             this.bodyEl.on('touchstart', this.onTouchStart, this);
27149             this.bodyEl.on('touchmove', this.onTouchMove, this);
27150             this.bodyEl.on('touchend', this.onTouchEnd, this);
27151         }
27152         
27153         if(!Roo.isTouch){
27154             this.bodyEl.on('mousedown', this.onMouseDown, this);
27155             this.bodyEl.on('mousemove', this.onMouseMove, this);
27156             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27157             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27158             Roo.get(document).on('mouseup', this.onMouseUp, this);
27159         }
27160         
27161         this.selectorEl.on('change', this.onFileSelected, this);
27162     },
27163     
27164     reset : function()
27165     {    
27166         this.scale = 0;
27167         this.baseScale = 1;
27168         this.rotate = 0;
27169         this.baseRotate = 1;
27170         this.dragable = false;
27171         this.pinching = false;
27172         this.mouseX = 0;
27173         this.mouseY = 0;
27174         this.cropData = false;
27175         this.notifyEl.dom.innerHTML = this.emptyText;
27176         
27177         this.selectorEl.dom.value = '';
27178         
27179     },
27180     
27181     resize : function()
27182     {
27183         if(this.fireEvent('resize', this) != false){
27184             this.setThumbBoxPosition();
27185             this.setCanvasPosition();
27186         }
27187     },
27188     
27189     onFooterButtonClick : function(e, el, o, type)
27190     {
27191         switch (type) {
27192             case 'rotate-left' :
27193                 this.onRotateLeft(e);
27194                 break;
27195             case 'rotate-right' :
27196                 this.onRotateRight(e);
27197                 break;
27198             case 'picture' :
27199                 this.beforeSelectFile(e);
27200                 break;
27201             case 'trash' :
27202                 this.trash(e);
27203                 break;
27204             case 'crop' :
27205                 this.crop(e);
27206                 break;
27207             case 'download' :
27208                 this.download(e);
27209                 break;
27210             default :
27211                 break;
27212         }
27213         
27214         this.fireEvent('footerbuttonclick', this, type);
27215     },
27216     
27217     beforeSelectFile : function(e)
27218     {
27219         e.preventDefault();
27220         
27221         if(this.fireEvent('beforeselectfile', this) != false){
27222             this.selectorEl.dom.click();
27223         }
27224     },
27225     
27226     onFileSelected : function(e)
27227     {
27228         e.preventDefault();
27229         
27230         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27231             return;
27232         }
27233         
27234         var file = this.selectorEl.dom.files[0];
27235         
27236         if(this.fireEvent('inspect', this, file) != false){
27237             this.prepare(file);
27238         }
27239         
27240     },
27241     
27242     trash : function(e)
27243     {
27244         this.fireEvent('trash', this);
27245     },
27246     
27247     download : function(e)
27248     {
27249         this.fireEvent('download', this);
27250     },
27251     
27252     loadCanvas : function(src)
27253     {   
27254         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27255             
27256             this.reset();
27257             
27258             this.imageEl = document.createElement('img');
27259             
27260             var _this = this;
27261             
27262             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27263             
27264             this.imageEl.src = src;
27265         }
27266     },
27267     
27268     onLoadCanvas : function()
27269     {   
27270         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27271         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27272         
27273         this.bodyEl.un('click', this.beforeSelectFile, this);
27274         
27275         this.notifyEl.hide();
27276         this.thumbEl.show();
27277         this.footerEl.show();
27278         
27279         this.baseRotateLevel();
27280         
27281         if(this.isDocument){
27282             this.setThumbBoxSize();
27283         }
27284         
27285         this.setThumbBoxPosition();
27286         
27287         this.baseScaleLevel();
27288         
27289         this.draw();
27290         
27291         this.resize();
27292         
27293         this.canvasLoaded = true;
27294         
27295         if(this.loadMask){
27296             this.maskEl.unmask();
27297         }
27298         
27299     },
27300     
27301     setCanvasPosition : function()
27302     {   
27303         if(!this.canvasEl){
27304             return;
27305         }
27306         
27307         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27308         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27309         
27310         this.previewEl.setLeft(pw);
27311         this.previewEl.setTop(ph);
27312         
27313     },
27314     
27315     onMouseDown : function(e)
27316     {   
27317         e.stopEvent();
27318         
27319         this.dragable = true;
27320         this.pinching = false;
27321         
27322         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27323             this.dragable = false;
27324             return;
27325         }
27326         
27327         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27328         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27329         
27330     },
27331     
27332     onMouseMove : function(e)
27333     {   
27334         e.stopEvent();
27335         
27336         if(!this.canvasLoaded){
27337             return;
27338         }
27339         
27340         if (!this.dragable){
27341             return;
27342         }
27343         
27344         var minX = Math.ceil(this.thumbEl.getLeft(true));
27345         var minY = Math.ceil(this.thumbEl.getTop(true));
27346         
27347         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27348         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27349         
27350         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27351         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27352         
27353         x = x - this.mouseX;
27354         y = y - this.mouseY;
27355         
27356         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27357         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27358         
27359         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27360         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27361         
27362         this.previewEl.setLeft(bgX);
27363         this.previewEl.setTop(bgY);
27364         
27365         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27366         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27367     },
27368     
27369     onMouseUp : function(e)
27370     {   
27371         e.stopEvent();
27372         
27373         this.dragable = false;
27374     },
27375     
27376     onMouseWheel : function(e)
27377     {   
27378         e.stopEvent();
27379         
27380         this.startScale = this.scale;
27381         
27382         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27383         
27384         if(!this.zoomable()){
27385             this.scale = this.startScale;
27386             return;
27387         }
27388         
27389         this.draw();
27390         
27391         return;
27392     },
27393     
27394     zoomable : function()
27395     {
27396         var minScale = this.thumbEl.getWidth() / this.minWidth;
27397         
27398         if(this.minWidth < this.minHeight){
27399             minScale = this.thumbEl.getHeight() / this.minHeight;
27400         }
27401         
27402         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27403         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27404         
27405         if(
27406                 this.isDocument &&
27407                 (this.rotate == 0 || this.rotate == 180) && 
27408                 (
27409                     width > this.imageEl.OriginWidth || 
27410                     height > this.imageEl.OriginHeight ||
27411                     (width < this.minWidth && height < this.minHeight)
27412                 )
27413         ){
27414             return false;
27415         }
27416         
27417         if(
27418                 this.isDocument &&
27419                 (this.rotate == 90 || this.rotate == 270) && 
27420                 (
27421                     width > this.imageEl.OriginWidth || 
27422                     height > this.imageEl.OriginHeight ||
27423                     (width < this.minHeight && height < this.minWidth)
27424                 )
27425         ){
27426             return false;
27427         }
27428         
27429         if(
27430                 !this.isDocument &&
27431                 (this.rotate == 0 || this.rotate == 180) && 
27432                 (
27433                     width < this.minWidth || 
27434                     width > this.imageEl.OriginWidth || 
27435                     height < this.minHeight || 
27436                     height > this.imageEl.OriginHeight
27437                 )
27438         ){
27439             return false;
27440         }
27441         
27442         if(
27443                 !this.isDocument &&
27444                 (this.rotate == 90 || this.rotate == 270) && 
27445                 (
27446                     width < this.minHeight || 
27447                     width > this.imageEl.OriginWidth || 
27448                     height < this.minWidth || 
27449                     height > this.imageEl.OriginHeight
27450                 )
27451         ){
27452             return false;
27453         }
27454         
27455         return true;
27456         
27457     },
27458     
27459     onRotateLeft : function(e)
27460     {   
27461         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27462             
27463             var minScale = this.thumbEl.getWidth() / this.minWidth;
27464             
27465             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27466             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27467             
27468             this.startScale = this.scale;
27469             
27470             while (this.getScaleLevel() < minScale){
27471             
27472                 this.scale = this.scale + 1;
27473                 
27474                 if(!this.zoomable()){
27475                     break;
27476                 }
27477                 
27478                 if(
27479                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27480                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27481                 ){
27482                     continue;
27483                 }
27484                 
27485                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27486
27487                 this.draw();
27488                 
27489                 return;
27490             }
27491             
27492             this.scale = this.startScale;
27493             
27494             this.onRotateFail();
27495             
27496             return false;
27497         }
27498         
27499         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27500
27501         if(this.isDocument){
27502             this.setThumbBoxSize();
27503             this.setThumbBoxPosition();
27504             this.setCanvasPosition();
27505         }
27506         
27507         this.draw();
27508         
27509         this.fireEvent('rotate', this, 'left');
27510         
27511     },
27512     
27513     onRotateRight : function(e)
27514     {
27515         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27516             
27517             var minScale = this.thumbEl.getWidth() / this.minWidth;
27518         
27519             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27520             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27521             
27522             this.startScale = this.scale;
27523             
27524             while (this.getScaleLevel() < minScale){
27525             
27526                 this.scale = this.scale + 1;
27527                 
27528                 if(!this.zoomable()){
27529                     break;
27530                 }
27531                 
27532                 if(
27533                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27534                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27535                 ){
27536                     continue;
27537                 }
27538                 
27539                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27540
27541                 this.draw();
27542                 
27543                 return;
27544             }
27545             
27546             this.scale = this.startScale;
27547             
27548             this.onRotateFail();
27549             
27550             return false;
27551         }
27552         
27553         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27554
27555         if(this.isDocument){
27556             this.setThumbBoxSize();
27557             this.setThumbBoxPosition();
27558             this.setCanvasPosition();
27559         }
27560         
27561         this.draw();
27562         
27563         this.fireEvent('rotate', this, 'right');
27564     },
27565     
27566     onRotateFail : function()
27567     {
27568         this.errorEl.show(true);
27569         
27570         var _this = this;
27571         
27572         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27573     },
27574     
27575     draw : function()
27576     {
27577         this.previewEl.dom.innerHTML = '';
27578         
27579         var canvasEl = document.createElement("canvas");
27580         
27581         var contextEl = canvasEl.getContext("2d");
27582         
27583         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27584         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27585         var center = this.imageEl.OriginWidth / 2;
27586         
27587         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27588             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27589             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27590             center = this.imageEl.OriginHeight / 2;
27591         }
27592         
27593         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27594         
27595         contextEl.translate(center, center);
27596         contextEl.rotate(this.rotate * Math.PI / 180);
27597
27598         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27599         
27600         this.canvasEl = document.createElement("canvas");
27601         
27602         this.contextEl = this.canvasEl.getContext("2d");
27603         
27604         switch (this.rotate) {
27605             case 0 :
27606                 
27607                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27608                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27609                 
27610                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27611                 
27612                 break;
27613             case 90 : 
27614                 
27615                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27616                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27617                 
27618                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27619                     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);
27620                     break;
27621                 }
27622                 
27623                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27624                 
27625                 break;
27626             case 180 :
27627                 
27628                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27629                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27630                 
27631                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27632                     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);
27633                     break;
27634                 }
27635                 
27636                 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);
27637                 
27638                 break;
27639             case 270 :
27640                 
27641                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27642                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27643         
27644                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27645                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27646                     break;
27647                 }
27648                 
27649                 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);
27650                 
27651                 break;
27652             default : 
27653                 break;
27654         }
27655         
27656         this.previewEl.appendChild(this.canvasEl);
27657         
27658         this.setCanvasPosition();
27659     },
27660     
27661     crop : function()
27662     {
27663         if(!this.canvasLoaded){
27664             return;
27665         }
27666         
27667         var imageCanvas = document.createElement("canvas");
27668         
27669         var imageContext = imageCanvas.getContext("2d");
27670         
27671         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27672         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27673         
27674         var center = imageCanvas.width / 2;
27675         
27676         imageContext.translate(center, center);
27677         
27678         imageContext.rotate(this.rotate * Math.PI / 180);
27679         
27680         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27681         
27682         var canvas = document.createElement("canvas");
27683         
27684         var context = canvas.getContext("2d");
27685                 
27686         canvas.width = this.minWidth;
27687         canvas.height = this.minHeight;
27688
27689         switch (this.rotate) {
27690             case 0 :
27691                 
27692                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27693                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27694                 
27695                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27696                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27697                 
27698                 var targetWidth = this.minWidth - 2 * x;
27699                 var targetHeight = this.minHeight - 2 * y;
27700                 
27701                 var scale = 1;
27702                 
27703                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27704                     scale = targetWidth / width;
27705                 }
27706                 
27707                 if(x > 0 && y == 0){
27708                     scale = targetHeight / height;
27709                 }
27710                 
27711                 if(x > 0 && y > 0){
27712                     scale = targetWidth / width;
27713                     
27714                     if(width < height){
27715                         scale = targetHeight / height;
27716                     }
27717                 }
27718                 
27719                 context.scale(scale, scale);
27720                 
27721                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27722                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27723
27724                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27725                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27726
27727                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27728                 
27729                 break;
27730             case 90 : 
27731                 
27732                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27733                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27734                 
27735                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27736                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27737                 
27738                 var targetWidth = this.minWidth - 2 * x;
27739                 var targetHeight = this.minHeight - 2 * y;
27740                 
27741                 var scale = 1;
27742                 
27743                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27744                     scale = targetWidth / width;
27745                 }
27746                 
27747                 if(x > 0 && y == 0){
27748                     scale = targetHeight / height;
27749                 }
27750                 
27751                 if(x > 0 && y > 0){
27752                     scale = targetWidth / width;
27753                     
27754                     if(width < height){
27755                         scale = targetHeight / height;
27756                     }
27757                 }
27758                 
27759                 context.scale(scale, scale);
27760                 
27761                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27762                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27763
27764                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27765                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27766                 
27767                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27768                 
27769                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27770                 
27771                 break;
27772             case 180 :
27773                 
27774                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27775                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27776                 
27777                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27778                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27779                 
27780                 var targetWidth = this.minWidth - 2 * x;
27781                 var targetHeight = this.minHeight - 2 * y;
27782                 
27783                 var scale = 1;
27784                 
27785                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27786                     scale = targetWidth / width;
27787                 }
27788                 
27789                 if(x > 0 && y == 0){
27790                     scale = targetHeight / height;
27791                 }
27792                 
27793                 if(x > 0 && y > 0){
27794                     scale = targetWidth / width;
27795                     
27796                     if(width < height){
27797                         scale = targetHeight / height;
27798                     }
27799                 }
27800                 
27801                 context.scale(scale, scale);
27802                 
27803                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27804                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27805
27806                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27807                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27808
27809                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27810                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27811                 
27812                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27813                 
27814                 break;
27815             case 270 :
27816                 
27817                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27818                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27819                 
27820                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27821                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27822                 
27823                 var targetWidth = this.minWidth - 2 * x;
27824                 var targetHeight = this.minHeight - 2 * y;
27825                 
27826                 var scale = 1;
27827                 
27828                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27829                     scale = targetWidth / width;
27830                 }
27831                 
27832                 if(x > 0 && y == 0){
27833                     scale = targetHeight / height;
27834                 }
27835                 
27836                 if(x > 0 && y > 0){
27837                     scale = targetWidth / width;
27838                     
27839                     if(width < height){
27840                         scale = targetHeight / height;
27841                     }
27842                 }
27843                 
27844                 context.scale(scale, scale);
27845                 
27846                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27847                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27848
27849                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27850                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27851                 
27852                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27853                 
27854                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27855                 
27856                 break;
27857             default : 
27858                 break;
27859         }
27860         
27861         this.cropData = canvas.toDataURL(this.cropType);
27862         
27863         if(this.fireEvent('crop', this, this.cropData) !== false){
27864             this.process(this.file, this.cropData);
27865         }
27866         
27867         return;
27868         
27869     },
27870     
27871     setThumbBoxSize : function()
27872     {
27873         var width, height;
27874         
27875         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27876             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27877             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27878             
27879             this.minWidth = width;
27880             this.minHeight = height;
27881             
27882             if(this.rotate == 90 || this.rotate == 270){
27883                 this.minWidth = height;
27884                 this.minHeight = width;
27885             }
27886         }
27887         
27888         height = 300;
27889         width = Math.ceil(this.minWidth * height / this.minHeight);
27890         
27891         if(this.minWidth > this.minHeight){
27892             width = 300;
27893             height = Math.ceil(this.minHeight * width / this.minWidth);
27894         }
27895         
27896         this.thumbEl.setStyle({
27897             width : width + 'px',
27898             height : height + 'px'
27899         });
27900
27901         return;
27902             
27903     },
27904     
27905     setThumbBoxPosition : function()
27906     {
27907         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27908         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27909         
27910         this.thumbEl.setLeft(x);
27911         this.thumbEl.setTop(y);
27912         
27913     },
27914     
27915     baseRotateLevel : function()
27916     {
27917         this.baseRotate = 1;
27918         
27919         if(
27920                 typeof(this.exif) != 'undefined' &&
27921                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27922                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27923         ){
27924             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27925         }
27926         
27927         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27928         
27929     },
27930     
27931     baseScaleLevel : function()
27932     {
27933         var width, height;
27934         
27935         if(this.isDocument){
27936             
27937             if(this.baseRotate == 6 || this.baseRotate == 8){
27938             
27939                 height = this.thumbEl.getHeight();
27940                 this.baseScale = height / this.imageEl.OriginWidth;
27941
27942                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27943                     width = this.thumbEl.getWidth();
27944                     this.baseScale = width / this.imageEl.OriginHeight;
27945                 }
27946
27947                 return;
27948             }
27949
27950             height = this.thumbEl.getHeight();
27951             this.baseScale = height / this.imageEl.OriginHeight;
27952
27953             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27954                 width = this.thumbEl.getWidth();
27955                 this.baseScale = width / this.imageEl.OriginWidth;
27956             }
27957
27958             return;
27959         }
27960         
27961         if(this.baseRotate == 6 || this.baseRotate == 8){
27962             
27963             width = this.thumbEl.getHeight();
27964             this.baseScale = width / this.imageEl.OriginHeight;
27965             
27966             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27967                 height = this.thumbEl.getWidth();
27968                 this.baseScale = height / this.imageEl.OriginHeight;
27969             }
27970             
27971             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27972                 height = this.thumbEl.getWidth();
27973                 this.baseScale = height / this.imageEl.OriginHeight;
27974                 
27975                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27976                     width = this.thumbEl.getHeight();
27977                     this.baseScale = width / this.imageEl.OriginWidth;
27978                 }
27979             }
27980             
27981             return;
27982         }
27983         
27984         width = this.thumbEl.getWidth();
27985         this.baseScale = width / this.imageEl.OriginWidth;
27986         
27987         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27988             height = this.thumbEl.getHeight();
27989             this.baseScale = height / this.imageEl.OriginHeight;
27990         }
27991         
27992         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27993             
27994             height = this.thumbEl.getHeight();
27995             this.baseScale = height / this.imageEl.OriginHeight;
27996             
27997             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27998                 width = this.thumbEl.getWidth();
27999                 this.baseScale = width / this.imageEl.OriginWidth;
28000             }
28001             
28002         }
28003         
28004         return;
28005     },
28006     
28007     getScaleLevel : function()
28008     {
28009         return this.baseScale * Math.pow(1.1, this.scale);
28010     },
28011     
28012     onTouchStart : function(e)
28013     {
28014         if(!this.canvasLoaded){
28015             this.beforeSelectFile(e);
28016             return;
28017         }
28018         
28019         var touches = e.browserEvent.touches;
28020         
28021         if(!touches){
28022             return;
28023         }
28024         
28025         if(touches.length == 1){
28026             this.onMouseDown(e);
28027             return;
28028         }
28029         
28030         if(touches.length != 2){
28031             return;
28032         }
28033         
28034         var coords = [];
28035         
28036         for(var i = 0, finger; finger = touches[i]; i++){
28037             coords.push(finger.pageX, finger.pageY);
28038         }
28039         
28040         var x = Math.pow(coords[0] - coords[2], 2);
28041         var y = Math.pow(coords[1] - coords[3], 2);
28042         
28043         this.startDistance = Math.sqrt(x + y);
28044         
28045         this.startScale = this.scale;
28046         
28047         this.pinching = true;
28048         this.dragable = false;
28049         
28050     },
28051     
28052     onTouchMove : function(e)
28053     {
28054         if(!this.pinching && !this.dragable){
28055             return;
28056         }
28057         
28058         var touches = e.browserEvent.touches;
28059         
28060         if(!touches){
28061             return;
28062         }
28063         
28064         if(this.dragable){
28065             this.onMouseMove(e);
28066             return;
28067         }
28068         
28069         var coords = [];
28070         
28071         for(var i = 0, finger; finger = touches[i]; i++){
28072             coords.push(finger.pageX, finger.pageY);
28073         }
28074         
28075         var x = Math.pow(coords[0] - coords[2], 2);
28076         var y = Math.pow(coords[1] - coords[3], 2);
28077         
28078         this.endDistance = Math.sqrt(x + y);
28079         
28080         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28081         
28082         if(!this.zoomable()){
28083             this.scale = this.startScale;
28084             return;
28085         }
28086         
28087         this.draw();
28088         
28089     },
28090     
28091     onTouchEnd : function(e)
28092     {
28093         this.pinching = false;
28094         this.dragable = false;
28095         
28096     },
28097     
28098     process : function(file, crop)
28099     {
28100         if(this.loadMask){
28101             this.maskEl.mask(this.loadingText);
28102         }
28103         
28104         this.xhr = new XMLHttpRequest();
28105         
28106         file.xhr = this.xhr;
28107
28108         this.xhr.open(this.method, this.url, true);
28109         
28110         var headers = {
28111             "Accept": "application/json",
28112             "Cache-Control": "no-cache",
28113             "X-Requested-With": "XMLHttpRequest"
28114         };
28115         
28116         for (var headerName in headers) {
28117             var headerValue = headers[headerName];
28118             if (headerValue) {
28119                 this.xhr.setRequestHeader(headerName, headerValue);
28120             }
28121         }
28122         
28123         var _this = this;
28124         
28125         this.xhr.onload = function()
28126         {
28127             _this.xhrOnLoad(_this.xhr);
28128         }
28129         
28130         this.xhr.onerror = function()
28131         {
28132             _this.xhrOnError(_this.xhr);
28133         }
28134         
28135         var formData = new FormData();
28136
28137         formData.append('returnHTML', 'NO');
28138         
28139         if(crop){
28140             formData.append('crop', crop);
28141         }
28142         
28143         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28144             formData.append(this.paramName, file, file.name);
28145         }
28146         
28147         if(typeof(file.filename) != 'undefined'){
28148             formData.append('filename', file.filename);
28149         }
28150         
28151         if(typeof(file.mimetype) != 'undefined'){
28152             formData.append('mimetype', file.mimetype);
28153         }
28154         
28155         if(this.fireEvent('arrange', this, formData) != false){
28156             this.xhr.send(formData);
28157         };
28158     },
28159     
28160     xhrOnLoad : function(xhr)
28161     {
28162         if(this.loadMask){
28163             this.maskEl.unmask();
28164         }
28165         
28166         if (xhr.readyState !== 4) {
28167             this.fireEvent('exception', this, xhr);
28168             return;
28169         }
28170
28171         var response = Roo.decode(xhr.responseText);
28172         
28173         if(!response.success){
28174             this.fireEvent('exception', this, xhr);
28175             return;
28176         }
28177         
28178         var response = Roo.decode(xhr.responseText);
28179         
28180         this.fireEvent('upload', this, response);
28181         
28182     },
28183     
28184     xhrOnError : function()
28185     {
28186         if(this.loadMask){
28187             this.maskEl.unmask();
28188         }
28189         
28190         Roo.log('xhr on error');
28191         
28192         var response = Roo.decode(xhr.responseText);
28193           
28194         Roo.log(response);
28195         
28196     },
28197     
28198     prepare : function(file)
28199     {   
28200         if(this.loadMask){
28201             this.maskEl.mask(this.loadingText);
28202         }
28203         
28204         this.file = false;
28205         this.exif = {};
28206         
28207         if(typeof(file) === 'string'){
28208             this.loadCanvas(file);
28209             return;
28210         }
28211         
28212         if(!file || !this.urlAPI){
28213             return;
28214         }
28215         
28216         this.file = file;
28217         this.cropType = file.type;
28218         
28219         var _this = this;
28220         
28221         if(this.fireEvent('prepare', this, this.file) != false){
28222             
28223             var reader = new FileReader();
28224             
28225             reader.onload = function (e) {
28226                 if (e.target.error) {
28227                     Roo.log(e.target.error);
28228                     return;
28229                 }
28230                 
28231                 var buffer = e.target.result,
28232                     dataView = new DataView(buffer),
28233                     offset = 2,
28234                     maxOffset = dataView.byteLength - 4,
28235                     markerBytes,
28236                     markerLength;
28237                 
28238                 if (dataView.getUint16(0) === 0xffd8) {
28239                     while (offset < maxOffset) {
28240                         markerBytes = dataView.getUint16(offset);
28241                         
28242                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28243                             markerLength = dataView.getUint16(offset + 2) + 2;
28244                             if (offset + markerLength > dataView.byteLength) {
28245                                 Roo.log('Invalid meta data: Invalid segment size.');
28246                                 break;
28247                             }
28248                             
28249                             if(markerBytes == 0xffe1){
28250                                 _this.parseExifData(
28251                                     dataView,
28252                                     offset,
28253                                     markerLength
28254                                 );
28255                             }
28256                             
28257                             offset += markerLength;
28258                             
28259                             continue;
28260                         }
28261                         
28262                         break;
28263                     }
28264                     
28265                 }
28266                 
28267                 var url = _this.urlAPI.createObjectURL(_this.file);
28268                 
28269                 _this.loadCanvas(url);
28270                 
28271                 return;
28272             }
28273             
28274             reader.readAsArrayBuffer(this.file);
28275             
28276         }
28277         
28278     },
28279     
28280     parseExifData : function(dataView, offset, length)
28281     {
28282         var tiffOffset = offset + 10,
28283             littleEndian,
28284             dirOffset;
28285     
28286         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28287             // No Exif data, might be XMP data instead
28288             return;
28289         }
28290         
28291         // Check for the ASCII code for "Exif" (0x45786966):
28292         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28293             // No Exif data, might be XMP data instead
28294             return;
28295         }
28296         if (tiffOffset + 8 > dataView.byteLength) {
28297             Roo.log('Invalid Exif data: Invalid segment size.');
28298             return;
28299         }
28300         // Check for the two null bytes:
28301         if (dataView.getUint16(offset + 8) !== 0x0000) {
28302             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28303             return;
28304         }
28305         // Check the byte alignment:
28306         switch (dataView.getUint16(tiffOffset)) {
28307         case 0x4949:
28308             littleEndian = true;
28309             break;
28310         case 0x4D4D:
28311             littleEndian = false;
28312             break;
28313         default:
28314             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28315             return;
28316         }
28317         // Check for the TIFF tag marker (0x002A):
28318         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28319             Roo.log('Invalid Exif data: Missing TIFF marker.');
28320             return;
28321         }
28322         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28323         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28324         
28325         this.parseExifTags(
28326             dataView,
28327             tiffOffset,
28328             tiffOffset + dirOffset,
28329             littleEndian
28330         );
28331     },
28332     
28333     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28334     {
28335         var tagsNumber,
28336             dirEndOffset,
28337             i;
28338         if (dirOffset + 6 > dataView.byteLength) {
28339             Roo.log('Invalid Exif data: Invalid directory offset.');
28340             return;
28341         }
28342         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28343         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28344         if (dirEndOffset + 4 > dataView.byteLength) {
28345             Roo.log('Invalid Exif data: Invalid directory size.');
28346             return;
28347         }
28348         for (i = 0; i < tagsNumber; i += 1) {
28349             this.parseExifTag(
28350                 dataView,
28351                 tiffOffset,
28352                 dirOffset + 2 + 12 * i, // tag offset
28353                 littleEndian
28354             );
28355         }
28356         // Return the offset to the next directory:
28357         return dataView.getUint32(dirEndOffset, littleEndian);
28358     },
28359     
28360     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28361     {
28362         var tag = dataView.getUint16(offset, littleEndian);
28363         
28364         this.exif[tag] = this.getExifValue(
28365             dataView,
28366             tiffOffset,
28367             offset,
28368             dataView.getUint16(offset + 2, littleEndian), // tag type
28369             dataView.getUint32(offset + 4, littleEndian), // tag length
28370             littleEndian
28371         );
28372     },
28373     
28374     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28375     {
28376         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28377             tagSize,
28378             dataOffset,
28379             values,
28380             i,
28381             str,
28382             c;
28383     
28384         if (!tagType) {
28385             Roo.log('Invalid Exif data: Invalid tag type.');
28386             return;
28387         }
28388         
28389         tagSize = tagType.size * length;
28390         // Determine if the value is contained in the dataOffset bytes,
28391         // or if the value at the dataOffset is a pointer to the actual data:
28392         dataOffset = tagSize > 4 ?
28393                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28394         if (dataOffset + tagSize > dataView.byteLength) {
28395             Roo.log('Invalid Exif data: Invalid data offset.');
28396             return;
28397         }
28398         if (length === 1) {
28399             return tagType.getValue(dataView, dataOffset, littleEndian);
28400         }
28401         values = [];
28402         for (i = 0; i < length; i += 1) {
28403             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28404         }
28405         
28406         if (tagType.ascii) {
28407             str = '';
28408             // Concatenate the chars:
28409             for (i = 0; i < values.length; i += 1) {
28410                 c = values[i];
28411                 // Ignore the terminating NULL byte(s):
28412                 if (c === '\u0000') {
28413                     break;
28414                 }
28415                 str += c;
28416             }
28417             return str;
28418         }
28419         return values;
28420     }
28421     
28422 });
28423
28424 Roo.apply(Roo.bootstrap.UploadCropbox, {
28425     tags : {
28426         'Orientation': 0x0112
28427     },
28428     
28429     Orientation: {
28430             1: 0, //'top-left',
28431 //            2: 'top-right',
28432             3: 180, //'bottom-right',
28433 //            4: 'bottom-left',
28434 //            5: 'left-top',
28435             6: 90, //'right-top',
28436 //            7: 'right-bottom',
28437             8: 270 //'left-bottom'
28438     },
28439     
28440     exifTagTypes : {
28441         // byte, 8-bit unsigned int:
28442         1: {
28443             getValue: function (dataView, dataOffset) {
28444                 return dataView.getUint8(dataOffset);
28445             },
28446             size: 1
28447         },
28448         // ascii, 8-bit byte:
28449         2: {
28450             getValue: function (dataView, dataOffset) {
28451                 return String.fromCharCode(dataView.getUint8(dataOffset));
28452             },
28453             size: 1,
28454             ascii: true
28455         },
28456         // short, 16 bit int:
28457         3: {
28458             getValue: function (dataView, dataOffset, littleEndian) {
28459                 return dataView.getUint16(dataOffset, littleEndian);
28460             },
28461             size: 2
28462         },
28463         // long, 32 bit int:
28464         4: {
28465             getValue: function (dataView, dataOffset, littleEndian) {
28466                 return dataView.getUint32(dataOffset, littleEndian);
28467             },
28468             size: 4
28469         },
28470         // rational = two long values, first is numerator, second is denominator:
28471         5: {
28472             getValue: function (dataView, dataOffset, littleEndian) {
28473                 return dataView.getUint32(dataOffset, littleEndian) /
28474                     dataView.getUint32(dataOffset + 4, littleEndian);
28475             },
28476             size: 8
28477         },
28478         // slong, 32 bit signed int:
28479         9: {
28480             getValue: function (dataView, dataOffset, littleEndian) {
28481                 return dataView.getInt32(dataOffset, littleEndian);
28482             },
28483             size: 4
28484         },
28485         // srational, two slongs, first is numerator, second is denominator:
28486         10: {
28487             getValue: function (dataView, dataOffset, littleEndian) {
28488                 return dataView.getInt32(dataOffset, littleEndian) /
28489                     dataView.getInt32(dataOffset + 4, littleEndian);
28490             },
28491             size: 8
28492         }
28493     },
28494     
28495     footer : {
28496         STANDARD : [
28497             {
28498                 tag : 'div',
28499                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28500                 action : 'rotate-left',
28501                 cn : [
28502                     {
28503                         tag : 'button',
28504                         cls : 'btn btn-default',
28505                         html : '<i class="fa fa-undo"></i>'
28506                     }
28507                 ]
28508             },
28509             {
28510                 tag : 'div',
28511                 cls : 'btn-group roo-upload-cropbox-picture',
28512                 action : 'picture',
28513                 cn : [
28514                     {
28515                         tag : 'button',
28516                         cls : 'btn btn-default',
28517                         html : '<i class="fa fa-picture-o"></i>'
28518                     }
28519                 ]
28520             },
28521             {
28522                 tag : 'div',
28523                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28524                 action : 'rotate-right',
28525                 cn : [
28526                     {
28527                         tag : 'button',
28528                         cls : 'btn btn-default',
28529                         html : '<i class="fa fa-repeat"></i>'
28530                     }
28531                 ]
28532             }
28533         ],
28534         DOCUMENT : [
28535             {
28536                 tag : 'div',
28537                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28538                 action : 'rotate-left',
28539                 cn : [
28540                     {
28541                         tag : 'button',
28542                         cls : 'btn btn-default',
28543                         html : '<i class="fa fa-undo"></i>'
28544                     }
28545                 ]
28546             },
28547             {
28548                 tag : 'div',
28549                 cls : 'btn-group roo-upload-cropbox-download',
28550                 action : 'download',
28551                 cn : [
28552                     {
28553                         tag : 'button',
28554                         cls : 'btn btn-default',
28555                         html : '<i class="fa fa-download"></i>'
28556                     }
28557                 ]
28558             },
28559             {
28560                 tag : 'div',
28561                 cls : 'btn-group roo-upload-cropbox-crop',
28562                 action : 'crop',
28563                 cn : [
28564                     {
28565                         tag : 'button',
28566                         cls : 'btn btn-default',
28567                         html : '<i class="fa fa-crop"></i>'
28568                     }
28569                 ]
28570             },
28571             {
28572                 tag : 'div',
28573                 cls : 'btn-group roo-upload-cropbox-trash',
28574                 action : 'trash',
28575                 cn : [
28576                     {
28577                         tag : 'button',
28578                         cls : 'btn btn-default',
28579                         html : '<i class="fa fa-trash"></i>'
28580                     }
28581                 ]
28582             },
28583             {
28584                 tag : 'div',
28585                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28586                 action : 'rotate-right',
28587                 cn : [
28588                     {
28589                         tag : 'button',
28590                         cls : 'btn btn-default',
28591                         html : '<i class="fa fa-repeat"></i>'
28592                     }
28593                 ]
28594             }
28595         ],
28596         ROTATOR : [
28597             {
28598                 tag : 'div',
28599                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28600                 action : 'rotate-left',
28601                 cn : [
28602                     {
28603                         tag : 'button',
28604                         cls : 'btn btn-default',
28605                         html : '<i class="fa fa-undo"></i>'
28606                     }
28607                 ]
28608             },
28609             {
28610                 tag : 'div',
28611                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28612                 action : 'rotate-right',
28613                 cn : [
28614                     {
28615                         tag : 'button',
28616                         cls : 'btn btn-default',
28617                         html : '<i class="fa fa-repeat"></i>'
28618                     }
28619                 ]
28620             }
28621         ]
28622     }
28623 });
28624
28625 /*
28626 * Licence: LGPL
28627 */
28628
28629 /**
28630  * @class Roo.bootstrap.DocumentManager
28631  * @extends Roo.bootstrap.Component
28632  * Bootstrap DocumentManager class
28633  * @cfg {String} paramName default 'imageUpload'
28634  * @cfg {String} toolTipName default 'filename'
28635  * @cfg {String} method default POST
28636  * @cfg {String} url action url
28637  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28638  * @cfg {Boolean} multiple multiple upload default true
28639  * @cfg {Number} thumbSize default 300
28640  * @cfg {String} fieldLabel
28641  * @cfg {Number} labelWidth default 4
28642  * @cfg {String} labelAlign (left|top) default left
28643  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28644 * @cfg {Number} labellg set the width of label (1-12)
28645  * @cfg {Number} labelmd set the width of label (1-12)
28646  * @cfg {Number} labelsm set the width of label (1-12)
28647  * @cfg {Number} labelxs set the width of label (1-12)
28648  * 
28649  * @constructor
28650  * Create a new DocumentManager
28651  * @param {Object} config The config object
28652  */
28653
28654 Roo.bootstrap.DocumentManager = function(config){
28655     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28656     
28657     this.files = [];
28658     this.delegates = [];
28659     
28660     this.addEvents({
28661         /**
28662          * @event initial
28663          * Fire when initial the DocumentManager
28664          * @param {Roo.bootstrap.DocumentManager} this
28665          */
28666         "initial" : true,
28667         /**
28668          * @event inspect
28669          * inspect selected file
28670          * @param {Roo.bootstrap.DocumentManager} this
28671          * @param {File} file
28672          */
28673         "inspect" : true,
28674         /**
28675          * @event exception
28676          * Fire when xhr load exception
28677          * @param {Roo.bootstrap.DocumentManager} this
28678          * @param {XMLHttpRequest} xhr
28679          */
28680         "exception" : true,
28681         /**
28682          * @event afterupload
28683          * Fire when xhr load exception
28684          * @param {Roo.bootstrap.DocumentManager} this
28685          * @param {XMLHttpRequest} xhr
28686          */
28687         "afterupload" : true,
28688         /**
28689          * @event prepare
28690          * prepare the form data
28691          * @param {Roo.bootstrap.DocumentManager} this
28692          * @param {Object} formData
28693          */
28694         "prepare" : true,
28695         /**
28696          * @event remove
28697          * Fire when remove the file
28698          * @param {Roo.bootstrap.DocumentManager} this
28699          * @param {Object} file
28700          */
28701         "remove" : true,
28702         /**
28703          * @event refresh
28704          * Fire after refresh the file
28705          * @param {Roo.bootstrap.DocumentManager} this
28706          */
28707         "refresh" : true,
28708         /**
28709          * @event click
28710          * Fire after click the image
28711          * @param {Roo.bootstrap.DocumentManager} this
28712          * @param {Object} file
28713          */
28714         "click" : true,
28715         /**
28716          * @event edit
28717          * Fire when upload a image and editable set to true
28718          * @param {Roo.bootstrap.DocumentManager} this
28719          * @param {Object} file
28720          */
28721         "edit" : true,
28722         /**
28723          * @event beforeselectfile
28724          * Fire before select file
28725          * @param {Roo.bootstrap.DocumentManager} this
28726          */
28727         "beforeselectfile" : true,
28728         /**
28729          * @event process
28730          * Fire before process file
28731          * @param {Roo.bootstrap.DocumentManager} this
28732          * @param {Object} file
28733          */
28734         "process" : true,
28735         /**
28736          * @event previewrendered
28737          * Fire when preview rendered
28738          * @param {Roo.bootstrap.DocumentManager} this
28739          * @param {Object} file
28740          */
28741         "previewrendered" : true,
28742         /**
28743          */
28744         "previewResize" : true
28745         
28746     });
28747 };
28748
28749 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28750     
28751     boxes : 0,
28752     inputName : '',
28753     thumbSize : 300,
28754     multiple : true,
28755     files : false,
28756     method : 'POST',
28757     url : '',
28758     paramName : 'imageUpload',
28759     toolTipName : 'filename',
28760     fieldLabel : '',
28761     labelWidth : 4,
28762     labelAlign : 'left',
28763     editable : true,
28764     delegates : false,
28765     xhr : false, 
28766     
28767     labellg : 0,
28768     labelmd : 0,
28769     labelsm : 0,
28770     labelxs : 0,
28771     
28772     getAutoCreate : function()
28773     {   
28774         var managerWidget = {
28775             tag : 'div',
28776             cls : 'roo-document-manager',
28777             cn : [
28778                 {
28779                     tag : 'input',
28780                     cls : 'roo-document-manager-selector',
28781                     type : 'file'
28782                 },
28783                 {
28784                     tag : 'div',
28785                     cls : 'roo-document-manager-uploader',
28786                     cn : [
28787                         {
28788                             tag : 'div',
28789                             cls : 'roo-document-manager-upload-btn',
28790                             html : '<i class="fa fa-plus"></i>'
28791                         }
28792                     ]
28793                     
28794                 }
28795             ]
28796         };
28797         
28798         var content = [
28799             {
28800                 tag : 'div',
28801                 cls : 'column col-md-12',
28802                 cn : managerWidget
28803             }
28804         ];
28805         
28806         if(this.fieldLabel.length){
28807             
28808             content = [
28809                 {
28810                     tag : 'div',
28811                     cls : 'column col-md-12',
28812                     html : this.fieldLabel
28813                 },
28814                 {
28815                     tag : 'div',
28816                     cls : 'column col-md-12',
28817                     cn : managerWidget
28818                 }
28819             ];
28820
28821             if(this.labelAlign == 'left'){
28822                 content = [
28823                     {
28824                         tag : 'div',
28825                         cls : 'column',
28826                         html : this.fieldLabel
28827                     },
28828                     {
28829                         tag : 'div',
28830                         cls : 'column',
28831                         cn : managerWidget
28832                     }
28833                 ];
28834                 
28835                 if(this.labelWidth > 12){
28836                     content[0].style = "width: " + this.labelWidth + 'px';
28837                 }
28838
28839                 if(this.labelWidth < 13 && this.labelmd == 0){
28840                     this.labelmd = this.labelWidth;
28841                 }
28842
28843                 if(this.labellg > 0){
28844                     content[0].cls += ' col-lg-' + this.labellg;
28845                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28846                 }
28847
28848                 if(this.labelmd > 0){
28849                     content[0].cls += ' col-md-' + this.labelmd;
28850                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28851                 }
28852
28853                 if(this.labelsm > 0){
28854                     content[0].cls += ' col-sm-' + this.labelsm;
28855                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28856                 }
28857
28858                 if(this.labelxs > 0){
28859                     content[0].cls += ' col-xs-' + this.labelxs;
28860                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28861                 }
28862                 
28863             }
28864         }
28865         
28866         var cfg = {
28867             tag : 'div',
28868             cls : 'row clearfix',
28869             cn : content
28870         };
28871         
28872         return cfg;
28873         
28874     },
28875     
28876     initEvents : function()
28877     {
28878         this.managerEl = this.el.select('.roo-document-manager', true).first();
28879         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28880         
28881         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28882         this.selectorEl.hide();
28883         
28884         if(this.multiple){
28885             this.selectorEl.attr('multiple', 'multiple');
28886         }
28887         
28888         this.selectorEl.on('change', this.onFileSelected, this);
28889         
28890         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28891         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28892         
28893         this.uploader.on('click', this.onUploaderClick, this);
28894         
28895         this.renderProgressDialog();
28896         
28897         var _this = this;
28898         
28899         window.addEventListener("resize", function() { _this.refresh(); } );
28900         
28901         this.fireEvent('initial', this);
28902     },
28903     
28904     renderProgressDialog : function()
28905     {
28906         var _this = this;
28907         
28908         this.progressDialog = new Roo.bootstrap.Modal({
28909             cls : 'roo-document-manager-progress-dialog',
28910             allow_close : false,
28911             title : '',
28912             buttons : [
28913                 {
28914                     name  :'cancel',
28915                     weight : 'danger',
28916                     html : 'Cancel'
28917                 }
28918             ], 
28919             listeners : { 
28920                 btnclick : function() {
28921                     _this.uploadCancel();
28922                     this.hide();
28923                 }
28924             }
28925         });
28926          
28927         this.progressDialog.render(Roo.get(document.body));
28928          
28929         this.progress = new Roo.bootstrap.Progress({
28930             cls : 'roo-document-manager-progress',
28931             active : true,
28932             striped : true
28933         });
28934         
28935         this.progress.render(this.progressDialog.getChildContainer());
28936         
28937         this.progressBar = new Roo.bootstrap.ProgressBar({
28938             cls : 'roo-document-manager-progress-bar',
28939             aria_valuenow : 0,
28940             aria_valuemin : 0,
28941             aria_valuemax : 12,
28942             panel : 'success'
28943         });
28944         
28945         this.progressBar.render(this.progress.getChildContainer());
28946     },
28947     
28948     onUploaderClick : function(e)
28949     {
28950         e.preventDefault();
28951      
28952         if(this.fireEvent('beforeselectfile', this) != false){
28953             this.selectorEl.dom.click();
28954         }
28955         
28956     },
28957     
28958     onFileSelected : function(e)
28959     {
28960         e.preventDefault();
28961         
28962         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28963             return;
28964         }
28965         
28966         Roo.each(this.selectorEl.dom.files, function(file){
28967             if(this.fireEvent('inspect', this, file) != false){
28968                 this.files.push(file);
28969             }
28970         }, this);
28971         
28972         this.queue();
28973         
28974     },
28975     
28976     queue : function()
28977     {
28978         this.selectorEl.dom.value = '';
28979         
28980         if(!this.files || !this.files.length){
28981             return;
28982         }
28983         
28984         if(this.boxes > 0 && this.files.length > this.boxes){
28985             this.files = this.files.slice(0, this.boxes);
28986         }
28987         
28988         this.uploader.show();
28989         
28990         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28991             this.uploader.hide();
28992         }
28993         
28994         var _this = this;
28995         
28996         var files = [];
28997         
28998         var docs = [];
28999         
29000         Roo.each(this.files, function(file){
29001             
29002             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29003                 var f = this.renderPreview(file);
29004                 files.push(f);
29005                 return;
29006             }
29007             
29008             if(file.type.indexOf('image') != -1){
29009                 this.delegates.push(
29010                     (function(){
29011                         _this.process(file);
29012                     }).createDelegate(this)
29013                 );
29014         
29015                 return;
29016             }
29017             
29018             docs.push(
29019                 (function(){
29020                     _this.process(file);
29021                 }).createDelegate(this)
29022             );
29023             
29024         }, this);
29025         
29026         this.files = files;
29027         
29028         this.delegates = this.delegates.concat(docs);
29029         
29030         if(!this.delegates.length){
29031             this.refresh();
29032             return;
29033         }
29034         
29035         this.progressBar.aria_valuemax = this.delegates.length;
29036         
29037         this.arrange();
29038         
29039         return;
29040     },
29041     
29042     arrange : function()
29043     {
29044         if(!this.delegates.length){
29045             this.progressDialog.hide();
29046             this.refresh();
29047             return;
29048         }
29049         
29050         var delegate = this.delegates.shift();
29051         
29052         this.progressDialog.show();
29053         
29054         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29055         
29056         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29057         
29058         delegate();
29059     },
29060     
29061     refresh : function()
29062     {
29063         this.uploader.show();
29064         
29065         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29066             this.uploader.hide();
29067         }
29068         
29069         Roo.isTouch ? this.closable(false) : this.closable(true);
29070         
29071         this.fireEvent('refresh', this);
29072     },
29073     
29074     onRemove : function(e, el, o)
29075     {
29076         e.preventDefault();
29077         
29078         this.fireEvent('remove', this, o);
29079         
29080     },
29081     
29082     remove : function(o)
29083     {
29084         var files = [];
29085         
29086         Roo.each(this.files, function(file){
29087             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29088                 files.push(file);
29089                 return;
29090             }
29091
29092             o.target.remove();
29093
29094         }, this);
29095         
29096         this.files = files;
29097         
29098         this.refresh();
29099     },
29100     
29101     clear : function()
29102     {
29103         Roo.each(this.files, function(file){
29104             if(!file.target){
29105                 return;
29106             }
29107             
29108             file.target.remove();
29109
29110         }, this);
29111         
29112         this.files = [];
29113         
29114         this.refresh();
29115     },
29116     
29117     onClick : function(e, el, o)
29118     {
29119         e.preventDefault();
29120         
29121         this.fireEvent('click', this, o);
29122         
29123     },
29124     
29125     closable : function(closable)
29126     {
29127         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29128             
29129             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29130             
29131             if(closable){
29132                 el.show();
29133                 return;
29134             }
29135             
29136             el.hide();
29137             
29138         }, this);
29139     },
29140     
29141     xhrOnLoad : function(xhr)
29142     {
29143         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29144             el.remove();
29145         }, this);
29146         
29147         if (xhr.readyState !== 4) {
29148             this.arrange();
29149             this.fireEvent('exception', this, xhr);
29150             return;
29151         }
29152
29153         var response = Roo.decode(xhr.responseText);
29154         
29155         if(!response.success){
29156             this.arrange();
29157             this.fireEvent('exception', this, xhr);
29158             return;
29159         }
29160         
29161         var file = this.renderPreview(response.data);
29162         
29163         this.files.push(file);
29164         
29165         this.arrange();
29166         
29167         this.fireEvent('afterupload', this, xhr);
29168         
29169     },
29170     
29171     xhrOnError : function(xhr)
29172     {
29173         Roo.log('xhr on error');
29174         
29175         var response = Roo.decode(xhr.responseText);
29176           
29177         Roo.log(response);
29178         
29179         this.arrange();
29180     },
29181     
29182     process : function(file)
29183     {
29184         if(this.fireEvent('process', this, file) !== false){
29185             if(this.editable && file.type.indexOf('image') != -1){
29186                 this.fireEvent('edit', this, file);
29187                 return;
29188             }
29189
29190             this.uploadStart(file, false);
29191
29192             return;
29193         }
29194         
29195     },
29196     
29197     uploadStart : function(file, crop)
29198     {
29199         this.xhr = new XMLHttpRequest();
29200         
29201         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29202             this.arrange();
29203             return;
29204         }
29205         
29206         file.xhr = this.xhr;
29207             
29208         this.managerEl.createChild({
29209             tag : 'div',
29210             cls : 'roo-document-manager-loading',
29211             cn : [
29212                 {
29213                     tag : 'div',
29214                     tooltip : file.name,
29215                     cls : 'roo-document-manager-thumb',
29216                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29217                 }
29218             ]
29219
29220         });
29221
29222         this.xhr.open(this.method, this.url, true);
29223         
29224         var headers = {
29225             "Accept": "application/json",
29226             "Cache-Control": "no-cache",
29227             "X-Requested-With": "XMLHttpRequest"
29228         };
29229         
29230         for (var headerName in headers) {
29231             var headerValue = headers[headerName];
29232             if (headerValue) {
29233                 this.xhr.setRequestHeader(headerName, headerValue);
29234             }
29235         }
29236         
29237         var _this = this;
29238         
29239         this.xhr.onload = function()
29240         {
29241             _this.xhrOnLoad(_this.xhr);
29242         }
29243         
29244         this.xhr.onerror = function()
29245         {
29246             _this.xhrOnError(_this.xhr);
29247         }
29248         
29249         var formData = new FormData();
29250
29251         formData.append('returnHTML', 'NO');
29252         
29253         if(crop){
29254             formData.append('crop', crop);
29255         }
29256         
29257         formData.append(this.paramName, file, file.name);
29258         
29259         var options = {
29260             file : file, 
29261             manually : false
29262         };
29263         
29264         if(this.fireEvent('prepare', this, formData, options) != false){
29265             
29266             if(options.manually){
29267                 return;
29268             }
29269             
29270             this.xhr.send(formData);
29271             return;
29272         };
29273         
29274         this.uploadCancel();
29275     },
29276     
29277     uploadCancel : function()
29278     {
29279         if (this.xhr) {
29280             this.xhr.abort();
29281         }
29282         
29283         this.delegates = [];
29284         
29285         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29286             el.remove();
29287         }, this);
29288         
29289         this.arrange();
29290     },
29291     
29292     renderPreview : function(file)
29293     {
29294         if(typeof(file.target) != 'undefined' && file.target){
29295             return file;
29296         }
29297         
29298         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29299         
29300         var previewEl = this.managerEl.createChild({
29301             tag : 'div',
29302             cls : 'roo-document-manager-preview',
29303             cn : [
29304                 {
29305                     tag : 'div',
29306                     tooltip : file[this.toolTipName],
29307                     cls : 'roo-document-manager-thumb',
29308                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29309                 },
29310                 {
29311                     tag : 'button',
29312                     cls : 'close',
29313                     html : '<i class="fa fa-times-circle"></i>'
29314                 }
29315             ]
29316         });
29317
29318         var close = previewEl.select('button.close', true).first();
29319
29320         close.on('click', this.onRemove, this, file);
29321
29322         file.target = previewEl;
29323
29324         var image = previewEl.select('img', true).first();
29325         
29326         var _this = this;
29327         
29328         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29329         
29330         image.on('click', this.onClick, this, file);
29331         
29332         this.fireEvent('previewrendered', this, file);
29333         
29334         return file;
29335         
29336     },
29337     
29338     onPreviewLoad : function(file, image)
29339     {
29340         if(typeof(file.target) == 'undefined' || !file.target){
29341             return;
29342         }
29343         
29344         var width = image.dom.naturalWidth || image.dom.width;
29345         var height = image.dom.naturalHeight || image.dom.height;
29346         
29347         if(!this.previewResize) {
29348             return;
29349         }
29350         
29351         if(width > height){
29352             file.target.addClass('wide');
29353             return;
29354         }
29355         
29356         file.target.addClass('tall');
29357         return;
29358         
29359     },
29360     
29361     uploadFromSource : function(file, crop)
29362     {
29363         this.xhr = new XMLHttpRequest();
29364         
29365         this.managerEl.createChild({
29366             tag : 'div',
29367             cls : 'roo-document-manager-loading',
29368             cn : [
29369                 {
29370                     tag : 'div',
29371                     tooltip : file.name,
29372                     cls : 'roo-document-manager-thumb',
29373                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29374                 }
29375             ]
29376
29377         });
29378
29379         this.xhr.open(this.method, this.url, true);
29380         
29381         var headers = {
29382             "Accept": "application/json",
29383             "Cache-Control": "no-cache",
29384             "X-Requested-With": "XMLHttpRequest"
29385         };
29386         
29387         for (var headerName in headers) {
29388             var headerValue = headers[headerName];
29389             if (headerValue) {
29390                 this.xhr.setRequestHeader(headerName, headerValue);
29391             }
29392         }
29393         
29394         var _this = this;
29395         
29396         this.xhr.onload = function()
29397         {
29398             _this.xhrOnLoad(_this.xhr);
29399         }
29400         
29401         this.xhr.onerror = function()
29402         {
29403             _this.xhrOnError(_this.xhr);
29404         }
29405         
29406         var formData = new FormData();
29407
29408         formData.append('returnHTML', 'NO');
29409         
29410         formData.append('crop', crop);
29411         
29412         if(typeof(file.filename) != 'undefined'){
29413             formData.append('filename', file.filename);
29414         }
29415         
29416         if(typeof(file.mimetype) != 'undefined'){
29417             formData.append('mimetype', file.mimetype);
29418         }
29419         
29420         Roo.log(formData);
29421         
29422         if(this.fireEvent('prepare', this, formData) != false){
29423             this.xhr.send(formData);
29424         };
29425     }
29426 });
29427
29428 /*
29429 * Licence: LGPL
29430 */
29431
29432 /**
29433  * @class Roo.bootstrap.DocumentViewer
29434  * @extends Roo.bootstrap.Component
29435  * Bootstrap DocumentViewer class
29436  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29437  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29438  * 
29439  * @constructor
29440  * Create a new DocumentViewer
29441  * @param {Object} config The config object
29442  */
29443
29444 Roo.bootstrap.DocumentViewer = function(config){
29445     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29446     
29447     this.addEvents({
29448         /**
29449          * @event initial
29450          * Fire after initEvent
29451          * @param {Roo.bootstrap.DocumentViewer} this
29452          */
29453         "initial" : true,
29454         /**
29455          * @event click
29456          * Fire after click
29457          * @param {Roo.bootstrap.DocumentViewer} this
29458          */
29459         "click" : true,
29460         /**
29461          * @event download
29462          * Fire after download button
29463          * @param {Roo.bootstrap.DocumentViewer} this
29464          */
29465         "download" : true,
29466         /**
29467          * @event trash
29468          * Fire after trash button
29469          * @param {Roo.bootstrap.DocumentViewer} this
29470          */
29471         "trash" : true
29472         
29473     });
29474 };
29475
29476 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29477     
29478     showDownload : true,
29479     
29480     showTrash : true,
29481     
29482     getAutoCreate : function()
29483     {
29484         var cfg = {
29485             tag : 'div',
29486             cls : 'roo-document-viewer',
29487             cn : [
29488                 {
29489                     tag : 'div',
29490                     cls : 'roo-document-viewer-body',
29491                     cn : [
29492                         {
29493                             tag : 'div',
29494                             cls : 'roo-document-viewer-thumb',
29495                             cn : [
29496                                 {
29497                                     tag : 'img',
29498                                     cls : 'roo-document-viewer-image'
29499                                 }
29500                             ]
29501                         }
29502                     ]
29503                 },
29504                 {
29505                     tag : 'div',
29506                     cls : 'roo-document-viewer-footer',
29507                     cn : {
29508                         tag : 'div',
29509                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29510                         cn : [
29511                             {
29512                                 tag : 'div',
29513                                 cls : 'btn-group roo-document-viewer-download',
29514                                 cn : [
29515                                     {
29516                                         tag : 'button',
29517                                         cls : 'btn btn-default',
29518                                         html : '<i class="fa fa-download"></i>'
29519                                     }
29520                                 ]
29521                             },
29522                             {
29523                                 tag : 'div',
29524                                 cls : 'btn-group roo-document-viewer-trash',
29525                                 cn : [
29526                                     {
29527                                         tag : 'button',
29528                                         cls : 'btn btn-default',
29529                                         html : '<i class="fa fa-trash"></i>'
29530                                     }
29531                                 ]
29532                             }
29533                         ]
29534                     }
29535                 }
29536             ]
29537         };
29538         
29539         return cfg;
29540     },
29541     
29542     initEvents : function()
29543     {
29544         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29545         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29546         
29547         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29548         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29549         
29550         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29551         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29552         
29553         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29554         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29555         
29556         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29557         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29558         
29559         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29560         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29561         
29562         this.bodyEl.on('click', this.onClick, this);
29563         this.downloadBtn.on('click', this.onDownload, this);
29564         this.trashBtn.on('click', this.onTrash, this);
29565         
29566         this.downloadBtn.hide();
29567         this.trashBtn.hide();
29568         
29569         if(this.showDownload){
29570             this.downloadBtn.show();
29571         }
29572         
29573         if(this.showTrash){
29574             this.trashBtn.show();
29575         }
29576         
29577         if(!this.showDownload && !this.showTrash) {
29578             this.footerEl.hide();
29579         }
29580         
29581     },
29582     
29583     initial : function()
29584     {
29585         this.fireEvent('initial', this);
29586         
29587     },
29588     
29589     onClick : function(e)
29590     {
29591         e.preventDefault();
29592         
29593         this.fireEvent('click', this);
29594     },
29595     
29596     onDownload : function(e)
29597     {
29598         e.preventDefault();
29599         
29600         this.fireEvent('download', this);
29601     },
29602     
29603     onTrash : function(e)
29604     {
29605         e.preventDefault();
29606         
29607         this.fireEvent('trash', this);
29608     }
29609     
29610 });
29611 /*
29612  * - LGPL
29613  *
29614  * nav progress bar
29615  * 
29616  */
29617
29618 /**
29619  * @class Roo.bootstrap.NavProgressBar
29620  * @extends Roo.bootstrap.Component
29621  * Bootstrap NavProgressBar class
29622  * 
29623  * @constructor
29624  * Create a new nav progress bar
29625  * @param {Object} config The config object
29626  */
29627
29628 Roo.bootstrap.NavProgressBar = function(config){
29629     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29630
29631     this.bullets = this.bullets || [];
29632    
29633 //    Roo.bootstrap.NavProgressBar.register(this);
29634      this.addEvents({
29635         /**
29636              * @event changed
29637              * Fires when the active item changes
29638              * @param {Roo.bootstrap.NavProgressBar} this
29639              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29640              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29641          */
29642         'changed': true
29643      });
29644     
29645 };
29646
29647 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29648     
29649     bullets : [],
29650     barItems : [],
29651     
29652     getAutoCreate : function()
29653     {
29654         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29655         
29656         cfg = {
29657             tag : 'div',
29658             cls : 'roo-navigation-bar-group',
29659             cn : [
29660                 {
29661                     tag : 'div',
29662                     cls : 'roo-navigation-top-bar'
29663                 },
29664                 {
29665                     tag : 'div',
29666                     cls : 'roo-navigation-bullets-bar',
29667                     cn : [
29668                         {
29669                             tag : 'ul',
29670                             cls : 'roo-navigation-bar'
29671                         }
29672                     ]
29673                 },
29674                 
29675                 {
29676                     tag : 'div',
29677                     cls : 'roo-navigation-bottom-bar'
29678                 }
29679             ]
29680             
29681         };
29682         
29683         return cfg;
29684         
29685     },
29686     
29687     initEvents: function() 
29688     {
29689         
29690     },
29691     
29692     onRender : function(ct, position) 
29693     {
29694         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29695         
29696         if(this.bullets.length){
29697             Roo.each(this.bullets, function(b){
29698                this.addItem(b);
29699             }, this);
29700         }
29701         
29702         this.format();
29703         
29704     },
29705     
29706     addItem : function(cfg)
29707     {
29708         var item = new Roo.bootstrap.NavProgressItem(cfg);
29709         
29710         item.parentId = this.id;
29711         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29712         
29713         if(cfg.html){
29714             var top = new Roo.bootstrap.Element({
29715                 tag : 'div',
29716                 cls : 'roo-navigation-bar-text'
29717             });
29718             
29719             var bottom = new Roo.bootstrap.Element({
29720                 tag : 'div',
29721                 cls : 'roo-navigation-bar-text'
29722             });
29723             
29724             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29725             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29726             
29727             var topText = new Roo.bootstrap.Element({
29728                 tag : 'span',
29729                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29730             });
29731             
29732             var bottomText = new Roo.bootstrap.Element({
29733                 tag : 'span',
29734                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29735             });
29736             
29737             topText.onRender(top.el, null);
29738             bottomText.onRender(bottom.el, null);
29739             
29740             item.topEl = top;
29741             item.bottomEl = bottom;
29742         }
29743         
29744         this.barItems.push(item);
29745         
29746         return item;
29747     },
29748     
29749     getActive : function()
29750     {
29751         var active = false;
29752         
29753         Roo.each(this.barItems, function(v){
29754             
29755             if (!v.isActive()) {
29756                 return;
29757             }
29758             
29759             active = v;
29760             return false;
29761             
29762         });
29763         
29764         return active;
29765     },
29766     
29767     setActiveItem : function(item)
29768     {
29769         var prev = false;
29770         
29771         Roo.each(this.barItems, function(v){
29772             if (v.rid == item.rid) {
29773                 return ;
29774             }
29775             
29776             if (v.isActive()) {
29777                 v.setActive(false);
29778                 prev = v;
29779             }
29780         });
29781
29782         item.setActive(true);
29783         
29784         this.fireEvent('changed', this, item, prev);
29785     },
29786     
29787     getBarItem: function(rid)
29788     {
29789         var ret = false;
29790         
29791         Roo.each(this.barItems, function(e) {
29792             if (e.rid != rid) {
29793                 return;
29794             }
29795             
29796             ret =  e;
29797             return false;
29798         });
29799         
29800         return ret;
29801     },
29802     
29803     indexOfItem : function(item)
29804     {
29805         var index = false;
29806         
29807         Roo.each(this.barItems, function(v, i){
29808             
29809             if (v.rid != item.rid) {
29810                 return;
29811             }
29812             
29813             index = i;
29814             return false
29815         });
29816         
29817         return index;
29818     },
29819     
29820     setActiveNext : function()
29821     {
29822         var i = this.indexOfItem(this.getActive());
29823         
29824         if (i > this.barItems.length) {
29825             return;
29826         }
29827         
29828         this.setActiveItem(this.barItems[i+1]);
29829     },
29830     
29831     setActivePrev : function()
29832     {
29833         var i = this.indexOfItem(this.getActive());
29834         
29835         if (i  < 1) {
29836             return;
29837         }
29838         
29839         this.setActiveItem(this.barItems[i-1]);
29840     },
29841     
29842     format : function()
29843     {
29844         if(!this.barItems.length){
29845             return;
29846         }
29847      
29848         var width = 100 / this.barItems.length;
29849         
29850         Roo.each(this.barItems, function(i){
29851             i.el.setStyle('width', width + '%');
29852             i.topEl.el.setStyle('width', width + '%');
29853             i.bottomEl.el.setStyle('width', width + '%');
29854         }, this);
29855         
29856     }
29857     
29858 });
29859 /*
29860  * - LGPL
29861  *
29862  * Nav Progress Item
29863  * 
29864  */
29865
29866 /**
29867  * @class Roo.bootstrap.NavProgressItem
29868  * @extends Roo.bootstrap.Component
29869  * Bootstrap NavProgressItem class
29870  * @cfg {String} rid the reference id
29871  * @cfg {Boolean} active (true|false) Is item active default false
29872  * @cfg {Boolean} disabled (true|false) Is item active default false
29873  * @cfg {String} html
29874  * @cfg {String} position (top|bottom) text position default bottom
29875  * @cfg {String} icon show icon instead of number
29876  * 
29877  * @constructor
29878  * Create a new NavProgressItem
29879  * @param {Object} config The config object
29880  */
29881 Roo.bootstrap.NavProgressItem = function(config){
29882     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29883     this.addEvents({
29884         // raw events
29885         /**
29886          * @event click
29887          * The raw click event for the entire grid.
29888          * @param {Roo.bootstrap.NavProgressItem} this
29889          * @param {Roo.EventObject} e
29890          */
29891         "click" : true
29892     });
29893    
29894 };
29895
29896 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29897     
29898     rid : '',
29899     active : false,
29900     disabled : false,
29901     html : '',
29902     position : 'bottom',
29903     icon : false,
29904     
29905     getAutoCreate : function()
29906     {
29907         var iconCls = 'roo-navigation-bar-item-icon';
29908         
29909         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29910         
29911         var cfg = {
29912             tag: 'li',
29913             cls: 'roo-navigation-bar-item',
29914             cn : [
29915                 {
29916                     tag : 'i',
29917                     cls : iconCls
29918                 }
29919             ]
29920         };
29921         
29922         if(this.active){
29923             cfg.cls += ' active';
29924         }
29925         if(this.disabled){
29926             cfg.cls += ' disabled';
29927         }
29928         
29929         return cfg;
29930     },
29931     
29932     disable : function()
29933     {
29934         this.setDisabled(true);
29935     },
29936     
29937     enable : function()
29938     {
29939         this.setDisabled(false);
29940     },
29941     
29942     initEvents: function() 
29943     {
29944         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29945         
29946         this.iconEl.on('click', this.onClick, this);
29947     },
29948     
29949     onClick : function(e)
29950     {
29951         e.preventDefault();
29952         
29953         if(this.disabled){
29954             return;
29955         }
29956         
29957         if(this.fireEvent('click', this, e) === false){
29958             return;
29959         };
29960         
29961         this.parent().setActiveItem(this);
29962     },
29963     
29964     isActive: function () 
29965     {
29966         return this.active;
29967     },
29968     
29969     setActive : function(state)
29970     {
29971         if(this.active == state){
29972             return;
29973         }
29974         
29975         this.active = state;
29976         
29977         if (state) {
29978             this.el.addClass('active');
29979             return;
29980         }
29981         
29982         this.el.removeClass('active');
29983         
29984         return;
29985     },
29986     
29987     setDisabled : function(state)
29988     {
29989         if(this.disabled == state){
29990             return;
29991         }
29992         
29993         this.disabled = state;
29994         
29995         if (state) {
29996             this.el.addClass('disabled');
29997             return;
29998         }
29999         
30000         this.el.removeClass('disabled');
30001     },
30002     
30003     tooltipEl : function()
30004     {
30005         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30006     }
30007 });
30008  
30009
30010  /*
30011  * - LGPL
30012  *
30013  * FieldLabel
30014  * 
30015  */
30016
30017 /**
30018  * @class Roo.bootstrap.FieldLabel
30019  * @extends Roo.bootstrap.Component
30020  * Bootstrap FieldLabel class
30021  * @cfg {String} html contents of the element
30022  * @cfg {String} tag tag of the element default label
30023  * @cfg {String} cls class of the element
30024  * @cfg {String} target label target 
30025  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30026  * @cfg {String} invalidClass default "text-warning"
30027  * @cfg {String} validClass default "text-success"
30028  * @cfg {String} iconTooltip default "This field is required"
30029  * @cfg {String} indicatorpos (left|right) default left
30030  * 
30031  * @constructor
30032  * Create a new FieldLabel
30033  * @param {Object} config The config object
30034  */
30035
30036 Roo.bootstrap.FieldLabel = function(config){
30037     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30038     
30039     this.addEvents({
30040             /**
30041              * @event invalid
30042              * Fires after the field has been marked as invalid.
30043              * @param {Roo.form.FieldLabel} this
30044              * @param {String} msg The validation message
30045              */
30046             invalid : true,
30047             /**
30048              * @event valid
30049              * Fires after the field has been validated with no errors.
30050              * @param {Roo.form.FieldLabel} this
30051              */
30052             valid : true
30053         });
30054 };
30055
30056 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30057     
30058     tag: 'label',
30059     cls: '',
30060     html: '',
30061     target: '',
30062     allowBlank : true,
30063     invalidClass : 'has-warning',
30064     validClass : 'has-success',
30065     iconTooltip : 'This field is required',
30066     indicatorpos : 'left',
30067     
30068     getAutoCreate : function(){
30069         
30070         var cls = "";
30071         if (!this.allowBlank) {
30072             cls  = "visible";
30073         }
30074         
30075         var cfg = {
30076             tag : this.tag,
30077             cls : 'roo-bootstrap-field-label ' + this.cls,
30078             for : this.target,
30079             cn : [
30080                 {
30081                     tag : 'i',
30082                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30083                     tooltip : this.iconTooltip
30084                 },
30085                 {
30086                     tag : 'span',
30087                     html : this.html
30088                 }
30089             ] 
30090         };
30091         
30092         if(this.indicatorpos == 'right'){
30093             var cfg = {
30094                 tag : this.tag,
30095                 cls : 'roo-bootstrap-field-label ' + this.cls,
30096                 for : this.target,
30097                 cn : [
30098                     {
30099                         tag : 'span',
30100                         html : this.html
30101                     },
30102                     {
30103                         tag : 'i',
30104                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30105                         tooltip : this.iconTooltip
30106                     }
30107                 ] 
30108             };
30109         }
30110         
30111         return cfg;
30112     },
30113     
30114     initEvents: function() 
30115     {
30116         Roo.bootstrap.Element.superclass.initEvents.call(this);
30117         
30118         this.indicator = this.indicatorEl();
30119         
30120         if(this.indicator){
30121             this.indicator.removeClass('visible');
30122             this.indicator.addClass('invisible');
30123         }
30124         
30125         Roo.bootstrap.FieldLabel.register(this);
30126     },
30127     
30128     indicatorEl : function()
30129     {
30130         var indicator = this.el.select('i.roo-required-indicator',true).first();
30131         
30132         if(!indicator){
30133             return false;
30134         }
30135         
30136         return indicator;
30137         
30138     },
30139     
30140     /**
30141      * Mark this field as valid
30142      */
30143     markValid : function()
30144     {
30145         if(this.indicator){
30146             this.indicator.removeClass('visible');
30147             this.indicator.addClass('invisible');
30148         }
30149         
30150         this.el.removeClass(this.invalidClass);
30151         
30152         this.el.addClass(this.validClass);
30153         
30154         this.fireEvent('valid', this);
30155     },
30156     
30157     /**
30158      * Mark this field as invalid
30159      * @param {String} msg The validation message
30160      */
30161     markInvalid : function(msg)
30162     {
30163         if(this.indicator){
30164             this.indicator.removeClass('invisible');
30165             this.indicator.addClass('visible');
30166         }
30167         
30168         this.el.removeClass(this.validClass);
30169         
30170         this.el.addClass(this.invalidClass);
30171         
30172         this.fireEvent('invalid', this, msg);
30173     }
30174     
30175    
30176 });
30177
30178 Roo.apply(Roo.bootstrap.FieldLabel, {
30179     
30180     groups: {},
30181     
30182      /**
30183     * register a FieldLabel Group
30184     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30185     */
30186     register : function(label)
30187     {
30188         if(this.groups.hasOwnProperty(label.target)){
30189             return;
30190         }
30191      
30192         this.groups[label.target] = label;
30193         
30194     },
30195     /**
30196     * fetch a FieldLabel Group based on the target
30197     * @param {string} target
30198     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30199     */
30200     get: function(target) {
30201         if (typeof(this.groups[target]) == 'undefined') {
30202             return false;
30203         }
30204         
30205         return this.groups[target] ;
30206     }
30207 });
30208
30209  
30210
30211  /*
30212  * - LGPL
30213  *
30214  * page DateSplitField.
30215  * 
30216  */
30217
30218
30219 /**
30220  * @class Roo.bootstrap.DateSplitField
30221  * @extends Roo.bootstrap.Component
30222  * Bootstrap DateSplitField class
30223  * @cfg {string} fieldLabel - the label associated
30224  * @cfg {Number} labelWidth set the width of label (0-12)
30225  * @cfg {String} labelAlign (top|left)
30226  * @cfg {Boolean} dayAllowBlank (true|false) default false
30227  * @cfg {Boolean} monthAllowBlank (true|false) default false
30228  * @cfg {Boolean} yearAllowBlank (true|false) default false
30229  * @cfg {string} dayPlaceholder 
30230  * @cfg {string} monthPlaceholder
30231  * @cfg {string} yearPlaceholder
30232  * @cfg {string} dayFormat default 'd'
30233  * @cfg {string} monthFormat default 'm'
30234  * @cfg {string} yearFormat default 'Y'
30235  * @cfg {Number} labellg set the width of label (1-12)
30236  * @cfg {Number} labelmd set the width of label (1-12)
30237  * @cfg {Number} labelsm set the width of label (1-12)
30238  * @cfg {Number} labelxs set the width of label (1-12)
30239
30240  *     
30241  * @constructor
30242  * Create a new DateSplitField
30243  * @param {Object} config The config object
30244  */
30245
30246 Roo.bootstrap.DateSplitField = function(config){
30247     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30248     
30249     this.addEvents({
30250         // raw events
30251          /**
30252          * @event years
30253          * getting the data of years
30254          * @param {Roo.bootstrap.DateSplitField} this
30255          * @param {Object} years
30256          */
30257         "years" : true,
30258         /**
30259          * @event days
30260          * getting the data of days
30261          * @param {Roo.bootstrap.DateSplitField} this
30262          * @param {Object} days
30263          */
30264         "days" : true,
30265         /**
30266          * @event invalid
30267          * Fires after the field has been marked as invalid.
30268          * @param {Roo.form.Field} this
30269          * @param {String} msg The validation message
30270          */
30271         invalid : true,
30272        /**
30273          * @event valid
30274          * Fires after the field has been validated with no errors.
30275          * @param {Roo.form.Field} this
30276          */
30277         valid : true
30278     });
30279 };
30280
30281 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30282     
30283     fieldLabel : '',
30284     labelAlign : 'top',
30285     labelWidth : 3,
30286     dayAllowBlank : false,
30287     monthAllowBlank : false,
30288     yearAllowBlank : false,
30289     dayPlaceholder : '',
30290     monthPlaceholder : '',
30291     yearPlaceholder : '',
30292     dayFormat : 'd',
30293     monthFormat : 'm',
30294     yearFormat : 'Y',
30295     isFormField : true,
30296     labellg : 0,
30297     labelmd : 0,
30298     labelsm : 0,
30299     labelxs : 0,
30300     
30301     getAutoCreate : function()
30302     {
30303         var cfg = {
30304             tag : 'div',
30305             cls : 'row roo-date-split-field-group',
30306             cn : [
30307                 {
30308                     tag : 'input',
30309                     type : 'hidden',
30310                     cls : 'form-hidden-field roo-date-split-field-group-value',
30311                     name : this.name
30312                 }
30313             ]
30314         };
30315         
30316         var labelCls = 'col-md-12';
30317         var contentCls = 'col-md-4';
30318         
30319         if(this.fieldLabel){
30320             
30321             var label = {
30322                 tag : 'div',
30323                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30324                 cn : [
30325                     {
30326                         tag : 'label',
30327                         html : this.fieldLabel
30328                     }
30329                 ]
30330             };
30331             
30332             if(this.labelAlign == 'left'){
30333             
30334                 if(this.labelWidth > 12){
30335                     label.style = "width: " + this.labelWidth + 'px';
30336                 }
30337
30338                 if(this.labelWidth < 13 && this.labelmd == 0){
30339                     this.labelmd = this.labelWidth;
30340                 }
30341
30342                 if(this.labellg > 0){
30343                     labelCls = ' col-lg-' + this.labellg;
30344                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30345                 }
30346
30347                 if(this.labelmd > 0){
30348                     labelCls = ' col-md-' + this.labelmd;
30349                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30350                 }
30351
30352                 if(this.labelsm > 0){
30353                     labelCls = ' col-sm-' + this.labelsm;
30354                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30355                 }
30356
30357                 if(this.labelxs > 0){
30358                     labelCls = ' col-xs-' + this.labelxs;
30359                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30360                 }
30361             }
30362             
30363             label.cls += ' ' + labelCls;
30364             
30365             cfg.cn.push(label);
30366         }
30367         
30368         Roo.each(['day', 'month', 'year'], function(t){
30369             cfg.cn.push({
30370                 tag : 'div',
30371                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30372             });
30373         }, this);
30374         
30375         return cfg;
30376     },
30377     
30378     inputEl: function ()
30379     {
30380         return this.el.select('.roo-date-split-field-group-value', true).first();
30381     },
30382     
30383     onRender : function(ct, position) 
30384     {
30385         var _this = this;
30386         
30387         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30388         
30389         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30390         
30391         this.dayField = new Roo.bootstrap.ComboBox({
30392             allowBlank : this.dayAllowBlank,
30393             alwaysQuery : true,
30394             displayField : 'value',
30395             editable : false,
30396             fieldLabel : '',
30397             forceSelection : true,
30398             mode : 'local',
30399             placeholder : this.dayPlaceholder,
30400             selectOnFocus : true,
30401             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30402             triggerAction : 'all',
30403             typeAhead : true,
30404             valueField : 'value',
30405             store : new Roo.data.SimpleStore({
30406                 data : (function() {    
30407                     var days = [];
30408                     _this.fireEvent('days', _this, days);
30409                     return days;
30410                 })(),
30411                 fields : [ 'value' ]
30412             }),
30413             listeners : {
30414                 select : function (_self, record, index)
30415                 {
30416                     _this.setValue(_this.getValue());
30417                 }
30418             }
30419         });
30420
30421         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30422         
30423         this.monthField = new Roo.bootstrap.MonthField({
30424             after : '<i class=\"fa fa-calendar\"></i>',
30425             allowBlank : this.monthAllowBlank,
30426             placeholder : this.monthPlaceholder,
30427             readOnly : true,
30428             listeners : {
30429                 render : function (_self)
30430                 {
30431                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30432                         e.preventDefault();
30433                         _self.focus();
30434                     });
30435                 },
30436                 select : function (_self, oldvalue, newvalue)
30437                 {
30438                     _this.setValue(_this.getValue());
30439                 }
30440             }
30441         });
30442         
30443         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30444         
30445         this.yearField = new Roo.bootstrap.ComboBox({
30446             allowBlank : this.yearAllowBlank,
30447             alwaysQuery : true,
30448             displayField : 'value',
30449             editable : false,
30450             fieldLabel : '',
30451             forceSelection : true,
30452             mode : 'local',
30453             placeholder : this.yearPlaceholder,
30454             selectOnFocus : true,
30455             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30456             triggerAction : 'all',
30457             typeAhead : true,
30458             valueField : 'value',
30459             store : new Roo.data.SimpleStore({
30460                 data : (function() {
30461                     var years = [];
30462                     _this.fireEvent('years', _this, years);
30463                     return years;
30464                 })(),
30465                 fields : [ 'value' ]
30466             }),
30467             listeners : {
30468                 select : function (_self, record, index)
30469                 {
30470                     _this.setValue(_this.getValue());
30471                 }
30472             }
30473         });
30474
30475         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30476     },
30477     
30478     setValue : function(v, format)
30479     {
30480         this.inputEl.dom.value = v;
30481         
30482         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30483         
30484         var d = Date.parseDate(v, f);
30485         
30486         if(!d){
30487             this.validate();
30488             return;
30489         }
30490         
30491         this.setDay(d.format(this.dayFormat));
30492         this.setMonth(d.format(this.monthFormat));
30493         this.setYear(d.format(this.yearFormat));
30494         
30495         this.validate();
30496         
30497         return;
30498     },
30499     
30500     setDay : function(v)
30501     {
30502         this.dayField.setValue(v);
30503         this.inputEl.dom.value = this.getValue();
30504         this.validate();
30505         return;
30506     },
30507     
30508     setMonth : function(v)
30509     {
30510         this.monthField.setValue(v, true);
30511         this.inputEl.dom.value = this.getValue();
30512         this.validate();
30513         return;
30514     },
30515     
30516     setYear : function(v)
30517     {
30518         this.yearField.setValue(v);
30519         this.inputEl.dom.value = this.getValue();
30520         this.validate();
30521         return;
30522     },
30523     
30524     getDay : function()
30525     {
30526         return this.dayField.getValue();
30527     },
30528     
30529     getMonth : function()
30530     {
30531         return this.monthField.getValue();
30532     },
30533     
30534     getYear : function()
30535     {
30536         return this.yearField.getValue();
30537     },
30538     
30539     getValue : function()
30540     {
30541         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30542         
30543         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30544         
30545         return date;
30546     },
30547     
30548     reset : function()
30549     {
30550         this.setDay('');
30551         this.setMonth('');
30552         this.setYear('');
30553         this.inputEl.dom.value = '';
30554         this.validate();
30555         return;
30556     },
30557     
30558     validate : function()
30559     {
30560         var d = this.dayField.validate();
30561         var m = this.monthField.validate();
30562         var y = this.yearField.validate();
30563         
30564         var valid = true;
30565         
30566         if(
30567                 (!this.dayAllowBlank && !d) ||
30568                 (!this.monthAllowBlank && !m) ||
30569                 (!this.yearAllowBlank && !y)
30570         ){
30571             valid = false;
30572         }
30573         
30574         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30575             return valid;
30576         }
30577         
30578         if(valid){
30579             this.markValid();
30580             return valid;
30581         }
30582         
30583         this.markInvalid();
30584         
30585         return valid;
30586     },
30587     
30588     markValid : function()
30589     {
30590         
30591         var label = this.el.select('label', true).first();
30592         var icon = this.el.select('i.fa-star', true).first();
30593
30594         if(label && icon){
30595             icon.remove();
30596         }
30597         
30598         this.fireEvent('valid', this);
30599     },
30600     
30601      /**
30602      * Mark this field as invalid
30603      * @param {String} msg The validation message
30604      */
30605     markInvalid : function(msg)
30606     {
30607         
30608         var label = this.el.select('label', true).first();
30609         var icon = this.el.select('i.fa-star', true).first();
30610
30611         if(label && !icon){
30612             this.el.select('.roo-date-split-field-label', true).createChild({
30613                 tag : 'i',
30614                 cls : 'text-danger fa fa-lg fa-star',
30615                 tooltip : 'This field is required',
30616                 style : 'margin-right:5px;'
30617             }, label, true);
30618         }
30619         
30620         this.fireEvent('invalid', this, msg);
30621     },
30622     
30623     clearInvalid : function()
30624     {
30625         var label = this.el.select('label', true).first();
30626         var icon = this.el.select('i.fa-star', true).first();
30627
30628         if(label && icon){
30629             icon.remove();
30630         }
30631         
30632         this.fireEvent('valid', this);
30633     },
30634     
30635     getName: function()
30636     {
30637         return this.name;
30638     }
30639     
30640 });
30641
30642  /**
30643  *
30644  * This is based on 
30645  * http://masonry.desandro.com
30646  *
30647  * The idea is to render all the bricks based on vertical width...
30648  *
30649  * The original code extends 'outlayer' - we might need to use that....
30650  * 
30651  */
30652
30653
30654 /**
30655  * @class Roo.bootstrap.LayoutMasonry
30656  * @extends Roo.bootstrap.Component
30657  * Bootstrap Layout Masonry class
30658  * 
30659  * @constructor
30660  * Create a new Element
30661  * @param {Object} config The config object
30662  */
30663
30664 Roo.bootstrap.LayoutMasonry = function(config){
30665     
30666     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30667     
30668     this.bricks = [];
30669     
30670     Roo.bootstrap.LayoutMasonry.register(this);
30671     
30672     this.addEvents({
30673         // raw events
30674         /**
30675          * @event layout
30676          * Fire after layout the items
30677          * @param {Roo.bootstrap.LayoutMasonry} this
30678          * @param {Roo.EventObject} e
30679          */
30680         "layout" : true
30681     });
30682     
30683 };
30684
30685 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30686     
30687     /**
30688      * @cfg {Boolean} isLayoutInstant = no animation?
30689      */   
30690     isLayoutInstant : false, // needed?
30691    
30692     /**
30693      * @cfg {Number} boxWidth  width of the columns
30694      */   
30695     boxWidth : 450,
30696     
30697       /**
30698      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30699      */   
30700     boxHeight : 0,
30701     
30702     /**
30703      * @cfg {Number} padWidth padding below box..
30704      */   
30705     padWidth : 10, 
30706     
30707     /**
30708      * @cfg {Number} gutter gutter width..
30709      */   
30710     gutter : 10,
30711     
30712      /**
30713      * @cfg {Number} maxCols maximum number of columns
30714      */   
30715     
30716     maxCols: 0,
30717     
30718     /**
30719      * @cfg {Boolean} isAutoInitial defalut true
30720      */   
30721     isAutoInitial : true, 
30722     
30723     containerWidth: 0,
30724     
30725     /**
30726      * @cfg {Boolean} isHorizontal defalut false
30727      */   
30728     isHorizontal : false, 
30729
30730     currentSize : null,
30731     
30732     tag: 'div',
30733     
30734     cls: '',
30735     
30736     bricks: null, //CompositeElement
30737     
30738     cols : 1,
30739     
30740     _isLayoutInited : false,
30741     
30742 //    isAlternative : false, // only use for vertical layout...
30743     
30744     /**
30745      * @cfg {Number} alternativePadWidth padding below box..
30746      */   
30747     alternativePadWidth : 50,
30748     
30749     selectedBrick : [],
30750     
30751     getAutoCreate : function(){
30752         
30753         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30754         
30755         var cfg = {
30756             tag: this.tag,
30757             cls: 'blog-masonary-wrapper ' + this.cls,
30758             cn : {
30759                 cls : 'mas-boxes masonary'
30760             }
30761         };
30762         
30763         return cfg;
30764     },
30765     
30766     getChildContainer: function( )
30767     {
30768         if (this.boxesEl) {
30769             return this.boxesEl;
30770         }
30771         
30772         this.boxesEl = this.el.select('.mas-boxes').first();
30773         
30774         return this.boxesEl;
30775     },
30776     
30777     
30778     initEvents : function()
30779     {
30780         var _this = this;
30781         
30782         if(this.isAutoInitial){
30783             Roo.log('hook children rendered');
30784             this.on('childrenrendered', function() {
30785                 Roo.log('children rendered');
30786                 _this.initial();
30787             } ,this);
30788         }
30789     },
30790     
30791     initial : function()
30792     {
30793         this.selectedBrick = [];
30794         
30795         this.currentSize = this.el.getBox(true);
30796         
30797         Roo.EventManager.onWindowResize(this.resize, this); 
30798
30799         if(!this.isAutoInitial){
30800             this.layout();
30801             return;
30802         }
30803         
30804         this.layout();
30805         
30806         return;
30807         //this.layout.defer(500,this);
30808         
30809     },
30810     
30811     resize : function()
30812     {
30813         var cs = this.el.getBox(true);
30814         
30815         if (
30816                 this.currentSize.width == cs.width && 
30817                 this.currentSize.x == cs.x && 
30818                 this.currentSize.height == cs.height && 
30819                 this.currentSize.y == cs.y 
30820         ) {
30821             Roo.log("no change in with or X or Y");
30822             return;
30823         }
30824         
30825         this.currentSize = cs;
30826         
30827         this.layout();
30828         
30829     },
30830     
30831     layout : function()
30832     {   
30833         this._resetLayout();
30834         
30835         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30836         
30837         this.layoutItems( isInstant );
30838       
30839         this._isLayoutInited = true;
30840         
30841         this.fireEvent('layout', this);
30842         
30843     },
30844     
30845     _resetLayout : function()
30846     {
30847         if(this.isHorizontal){
30848             this.horizontalMeasureColumns();
30849             return;
30850         }
30851         
30852         this.verticalMeasureColumns();
30853         
30854     },
30855     
30856     verticalMeasureColumns : function()
30857     {
30858         this.getContainerWidth();
30859         
30860 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30861 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30862 //            return;
30863 //        }
30864         
30865         var boxWidth = this.boxWidth + this.padWidth;
30866         
30867         if(this.containerWidth < this.boxWidth){
30868             boxWidth = this.containerWidth
30869         }
30870         
30871         var containerWidth = this.containerWidth;
30872         
30873         var cols = Math.floor(containerWidth / boxWidth);
30874         
30875         this.cols = Math.max( cols, 1 );
30876         
30877         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30878         
30879         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30880         
30881         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30882         
30883         this.colWidth = boxWidth + avail - this.padWidth;
30884         
30885         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30886         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30887     },
30888     
30889     horizontalMeasureColumns : function()
30890     {
30891         this.getContainerWidth();
30892         
30893         var boxWidth = this.boxWidth;
30894         
30895         if(this.containerWidth < boxWidth){
30896             boxWidth = this.containerWidth;
30897         }
30898         
30899         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30900         
30901         this.el.setHeight(boxWidth);
30902         
30903     },
30904     
30905     getContainerWidth : function()
30906     {
30907         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30908     },
30909     
30910     layoutItems : function( isInstant )
30911     {
30912         Roo.log(this.bricks);
30913         
30914         var items = Roo.apply([], this.bricks);
30915         
30916         if(this.isHorizontal){
30917             this._horizontalLayoutItems( items , isInstant );
30918             return;
30919         }
30920         
30921 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30922 //            this._verticalAlternativeLayoutItems( items , isInstant );
30923 //            return;
30924 //        }
30925         
30926         this._verticalLayoutItems( items , isInstant );
30927         
30928     },
30929     
30930     _verticalLayoutItems : function ( items , isInstant)
30931     {
30932         if ( !items || !items.length ) {
30933             return;
30934         }
30935         
30936         var standard = [
30937             ['xs', 'xs', 'xs', 'tall'],
30938             ['xs', 'xs', 'tall'],
30939             ['xs', 'xs', 'sm'],
30940             ['xs', 'xs', 'xs'],
30941             ['xs', 'tall'],
30942             ['xs', 'sm'],
30943             ['xs', 'xs'],
30944             ['xs'],
30945             
30946             ['sm', 'xs', 'xs'],
30947             ['sm', 'xs'],
30948             ['sm'],
30949             
30950             ['tall', 'xs', 'xs', 'xs'],
30951             ['tall', 'xs', 'xs'],
30952             ['tall', 'xs'],
30953             ['tall']
30954             
30955         ];
30956         
30957         var queue = [];
30958         
30959         var boxes = [];
30960         
30961         var box = [];
30962         
30963         Roo.each(items, function(item, k){
30964             
30965             switch (item.size) {
30966                 // these layouts take up a full box,
30967                 case 'md' :
30968                 case 'md-left' :
30969                 case 'md-right' :
30970                 case 'wide' :
30971                     
30972                     if(box.length){
30973                         boxes.push(box);
30974                         box = [];
30975                     }
30976                     
30977                     boxes.push([item]);
30978                     
30979                     break;
30980                     
30981                 case 'xs' :
30982                 case 'sm' :
30983                 case 'tall' :
30984                     
30985                     box.push(item);
30986                     
30987                     break;
30988                 default :
30989                     break;
30990                     
30991             }
30992             
30993         }, this);
30994         
30995         if(box.length){
30996             boxes.push(box);
30997             box = [];
30998         }
30999         
31000         var filterPattern = function(box, length)
31001         {
31002             if(!box.length){
31003                 return;
31004             }
31005             
31006             var match = false;
31007             
31008             var pattern = box.slice(0, length);
31009             
31010             var format = [];
31011             
31012             Roo.each(pattern, function(i){
31013                 format.push(i.size);
31014             }, this);
31015             
31016             Roo.each(standard, function(s){
31017                 
31018                 if(String(s) != String(format)){
31019                     return;
31020                 }
31021                 
31022                 match = true;
31023                 return false;
31024                 
31025             }, this);
31026             
31027             if(!match && length == 1){
31028                 return;
31029             }
31030             
31031             if(!match){
31032                 filterPattern(box, length - 1);
31033                 return;
31034             }
31035                 
31036             queue.push(pattern);
31037
31038             box = box.slice(length, box.length);
31039
31040             filterPattern(box, 4);
31041
31042             return;
31043             
31044         }
31045         
31046         Roo.each(boxes, function(box, k){
31047             
31048             if(!box.length){
31049                 return;
31050             }
31051             
31052             if(box.length == 1){
31053                 queue.push(box);
31054                 return;
31055             }
31056             
31057             filterPattern(box, 4);
31058             
31059         }, this);
31060         
31061         this._processVerticalLayoutQueue( queue, isInstant );
31062         
31063     },
31064     
31065 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31066 //    {
31067 //        if ( !items || !items.length ) {
31068 //            return;
31069 //        }
31070 //
31071 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31072 //        
31073 //    },
31074     
31075     _horizontalLayoutItems : function ( items , isInstant)
31076     {
31077         if ( !items || !items.length || items.length < 3) {
31078             return;
31079         }
31080         
31081         items.reverse();
31082         
31083         var eItems = items.slice(0, 3);
31084         
31085         items = items.slice(3, items.length);
31086         
31087         var standard = [
31088             ['xs', 'xs', 'xs', 'wide'],
31089             ['xs', 'xs', 'wide'],
31090             ['xs', 'xs', 'sm'],
31091             ['xs', 'xs', 'xs'],
31092             ['xs', 'wide'],
31093             ['xs', 'sm'],
31094             ['xs', 'xs'],
31095             ['xs'],
31096             
31097             ['sm', 'xs', 'xs'],
31098             ['sm', 'xs'],
31099             ['sm'],
31100             
31101             ['wide', 'xs', 'xs', 'xs'],
31102             ['wide', 'xs', 'xs'],
31103             ['wide', 'xs'],
31104             ['wide'],
31105             
31106             ['wide-thin']
31107         ];
31108         
31109         var queue = [];
31110         
31111         var boxes = [];
31112         
31113         var box = [];
31114         
31115         Roo.each(items, function(item, k){
31116             
31117             switch (item.size) {
31118                 case 'md' :
31119                 case 'md-left' :
31120                 case 'md-right' :
31121                 case 'tall' :
31122                     
31123                     if(box.length){
31124                         boxes.push(box);
31125                         box = [];
31126                     }
31127                     
31128                     boxes.push([item]);
31129                     
31130                     break;
31131                     
31132                 case 'xs' :
31133                 case 'sm' :
31134                 case 'wide' :
31135                 case 'wide-thin' :
31136                     
31137                     box.push(item);
31138                     
31139                     break;
31140                 default :
31141                     break;
31142                     
31143             }
31144             
31145         }, this);
31146         
31147         if(box.length){
31148             boxes.push(box);
31149             box = [];
31150         }
31151         
31152         var filterPattern = function(box, length)
31153         {
31154             if(!box.length){
31155                 return;
31156             }
31157             
31158             var match = false;
31159             
31160             var pattern = box.slice(0, length);
31161             
31162             var format = [];
31163             
31164             Roo.each(pattern, function(i){
31165                 format.push(i.size);
31166             }, this);
31167             
31168             Roo.each(standard, function(s){
31169                 
31170                 if(String(s) != String(format)){
31171                     return;
31172                 }
31173                 
31174                 match = true;
31175                 return false;
31176                 
31177             }, this);
31178             
31179             if(!match && length == 1){
31180                 return;
31181             }
31182             
31183             if(!match){
31184                 filterPattern(box, length - 1);
31185                 return;
31186             }
31187                 
31188             queue.push(pattern);
31189
31190             box = box.slice(length, box.length);
31191
31192             filterPattern(box, 4);
31193
31194             return;
31195             
31196         }
31197         
31198         Roo.each(boxes, function(box, k){
31199             
31200             if(!box.length){
31201                 return;
31202             }
31203             
31204             if(box.length == 1){
31205                 queue.push(box);
31206                 return;
31207             }
31208             
31209             filterPattern(box, 4);
31210             
31211         }, this);
31212         
31213         
31214         var prune = [];
31215         
31216         var pos = this.el.getBox(true);
31217         
31218         var minX = pos.x;
31219         
31220         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31221         
31222         var hit_end = false;
31223         
31224         Roo.each(queue, function(box){
31225             
31226             if(hit_end){
31227                 
31228                 Roo.each(box, function(b){
31229                 
31230                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31231                     b.el.hide();
31232
31233                 }, this);
31234
31235                 return;
31236             }
31237             
31238             var mx = 0;
31239             
31240             Roo.each(box, function(b){
31241                 
31242                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31243                 b.el.show();
31244
31245                 mx = Math.max(mx, b.x);
31246                 
31247             }, this);
31248             
31249             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31250             
31251             if(maxX < minX){
31252                 
31253                 Roo.each(box, function(b){
31254                 
31255                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31256                     b.el.hide();
31257                     
31258                 }, this);
31259                 
31260                 hit_end = true;
31261                 
31262                 return;
31263             }
31264             
31265             prune.push(box);
31266             
31267         }, this);
31268         
31269         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31270     },
31271     
31272     /** Sets position of item in DOM
31273     * @param {Element} item
31274     * @param {Number} x - horizontal position
31275     * @param {Number} y - vertical position
31276     * @param {Boolean} isInstant - disables transitions
31277     */
31278     _processVerticalLayoutQueue : function( queue, isInstant )
31279     {
31280         var pos = this.el.getBox(true);
31281         var x = pos.x;
31282         var y = pos.y;
31283         var maxY = [];
31284         
31285         for (var i = 0; i < this.cols; i++){
31286             maxY[i] = pos.y;
31287         }
31288         
31289         Roo.each(queue, function(box, k){
31290             
31291             var col = k % this.cols;
31292             
31293             Roo.each(box, function(b,kk){
31294                 
31295                 b.el.position('absolute');
31296                 
31297                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31298                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31299                 
31300                 if(b.size == 'md-left' || b.size == 'md-right'){
31301                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31302                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31303                 }
31304                 
31305                 b.el.setWidth(width);
31306                 b.el.setHeight(height);
31307                 // iframe?
31308                 b.el.select('iframe',true).setSize(width,height);
31309                 
31310             }, this);
31311             
31312             for (var i = 0; i < this.cols; i++){
31313                 
31314                 if(maxY[i] < maxY[col]){
31315                     col = i;
31316                     continue;
31317                 }
31318                 
31319                 col = Math.min(col, i);
31320                 
31321             }
31322             
31323             x = pos.x + col * (this.colWidth + this.padWidth);
31324             
31325             y = maxY[col];
31326             
31327             var positions = [];
31328             
31329             switch (box.length){
31330                 case 1 :
31331                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31332                     break;
31333                 case 2 :
31334                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31335                     break;
31336                 case 3 :
31337                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31338                     break;
31339                 case 4 :
31340                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31341                     break;
31342                 default :
31343                     break;
31344             }
31345             
31346             Roo.each(box, function(b,kk){
31347                 
31348                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31349                 
31350                 var sz = b.el.getSize();
31351                 
31352                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31353                 
31354             }, this);
31355             
31356         }, this);
31357         
31358         var mY = 0;
31359         
31360         for (var i = 0; i < this.cols; i++){
31361             mY = Math.max(mY, maxY[i]);
31362         }
31363         
31364         this.el.setHeight(mY - pos.y);
31365         
31366     },
31367     
31368 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31369 //    {
31370 //        var pos = this.el.getBox(true);
31371 //        var x = pos.x;
31372 //        var y = pos.y;
31373 //        var maxX = pos.right;
31374 //        
31375 //        var maxHeight = 0;
31376 //        
31377 //        Roo.each(items, function(item, k){
31378 //            
31379 //            var c = k % 2;
31380 //            
31381 //            item.el.position('absolute');
31382 //                
31383 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31384 //
31385 //            item.el.setWidth(width);
31386 //
31387 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31388 //
31389 //            item.el.setHeight(height);
31390 //            
31391 //            if(c == 0){
31392 //                item.el.setXY([x, y], isInstant ? false : true);
31393 //            } else {
31394 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31395 //            }
31396 //            
31397 //            y = y + height + this.alternativePadWidth;
31398 //            
31399 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31400 //            
31401 //        }, this);
31402 //        
31403 //        this.el.setHeight(maxHeight);
31404 //        
31405 //    },
31406     
31407     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31408     {
31409         var pos = this.el.getBox(true);
31410         
31411         var minX = pos.x;
31412         var minY = pos.y;
31413         
31414         var maxX = pos.right;
31415         
31416         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31417         
31418         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31419         
31420         Roo.each(queue, function(box, k){
31421             
31422             Roo.each(box, function(b, kk){
31423                 
31424                 b.el.position('absolute');
31425                 
31426                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31427                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31428                 
31429                 if(b.size == 'md-left' || b.size == 'md-right'){
31430                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31431                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31432                 }
31433                 
31434                 b.el.setWidth(width);
31435                 b.el.setHeight(height);
31436                 
31437             }, this);
31438             
31439             if(!box.length){
31440                 return;
31441             }
31442             
31443             var positions = [];
31444             
31445             switch (box.length){
31446                 case 1 :
31447                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31448                     break;
31449                 case 2 :
31450                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31451                     break;
31452                 case 3 :
31453                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31454                     break;
31455                 case 4 :
31456                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31457                     break;
31458                 default :
31459                     break;
31460             }
31461             
31462             Roo.each(box, function(b,kk){
31463                 
31464                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31465                 
31466                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31467                 
31468             }, this);
31469             
31470         }, this);
31471         
31472     },
31473     
31474     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31475     {
31476         Roo.each(eItems, function(b,k){
31477             
31478             b.size = (k == 0) ? 'sm' : 'xs';
31479             b.x = (k == 0) ? 2 : 1;
31480             b.y = (k == 0) ? 2 : 1;
31481             
31482             b.el.position('absolute');
31483             
31484             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31485                 
31486             b.el.setWidth(width);
31487             
31488             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31489             
31490             b.el.setHeight(height);
31491             
31492         }, this);
31493
31494         var positions = [];
31495         
31496         positions.push({
31497             x : maxX - this.unitWidth * 2 - this.gutter,
31498             y : minY
31499         });
31500         
31501         positions.push({
31502             x : maxX - this.unitWidth,
31503             y : minY + (this.unitWidth + this.gutter) * 2
31504         });
31505         
31506         positions.push({
31507             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31508             y : minY
31509         });
31510         
31511         Roo.each(eItems, function(b,k){
31512             
31513             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31514
31515         }, this);
31516         
31517     },
31518     
31519     getVerticalOneBoxColPositions : function(x, y, box)
31520     {
31521         var pos = [];
31522         
31523         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31524         
31525         if(box[0].size == 'md-left'){
31526             rand = 0;
31527         }
31528         
31529         if(box[0].size == 'md-right'){
31530             rand = 1;
31531         }
31532         
31533         pos.push({
31534             x : x + (this.unitWidth + this.gutter) * rand,
31535             y : y
31536         });
31537         
31538         return pos;
31539     },
31540     
31541     getVerticalTwoBoxColPositions : function(x, y, box)
31542     {
31543         var pos = [];
31544         
31545         if(box[0].size == 'xs'){
31546             
31547             pos.push({
31548                 x : x,
31549                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31550             });
31551
31552             pos.push({
31553                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31554                 y : y
31555             });
31556             
31557             return pos;
31558             
31559         }
31560         
31561         pos.push({
31562             x : x,
31563             y : y
31564         });
31565
31566         pos.push({
31567             x : x + (this.unitWidth + this.gutter) * 2,
31568             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31569         });
31570         
31571         return pos;
31572         
31573     },
31574     
31575     getVerticalThreeBoxColPositions : function(x, y, box)
31576     {
31577         var pos = [];
31578         
31579         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31580             
31581             pos.push({
31582                 x : x,
31583                 y : y
31584             });
31585
31586             pos.push({
31587                 x : x + (this.unitWidth + this.gutter) * 1,
31588                 y : y
31589             });
31590             
31591             pos.push({
31592                 x : x + (this.unitWidth + this.gutter) * 2,
31593                 y : y
31594             });
31595             
31596             return pos;
31597             
31598         }
31599         
31600         if(box[0].size == 'xs' && box[1].size == 'xs'){
31601             
31602             pos.push({
31603                 x : x,
31604                 y : y
31605             });
31606
31607             pos.push({
31608                 x : x,
31609                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31610             });
31611             
31612             pos.push({
31613                 x : x + (this.unitWidth + this.gutter) * 1,
31614                 y : y
31615             });
31616             
31617             return pos;
31618             
31619         }
31620         
31621         pos.push({
31622             x : x,
31623             y : y
31624         });
31625
31626         pos.push({
31627             x : x + (this.unitWidth + this.gutter) * 2,
31628             y : y
31629         });
31630
31631         pos.push({
31632             x : x + (this.unitWidth + this.gutter) * 2,
31633             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31634         });
31635             
31636         return pos;
31637         
31638     },
31639     
31640     getVerticalFourBoxColPositions : function(x, y, box)
31641     {
31642         var pos = [];
31643         
31644         if(box[0].size == 'xs'){
31645             
31646             pos.push({
31647                 x : x,
31648                 y : y
31649             });
31650
31651             pos.push({
31652                 x : x,
31653                 y : y + (this.unitHeight + this.gutter) * 1
31654             });
31655             
31656             pos.push({
31657                 x : x,
31658                 y : y + (this.unitHeight + this.gutter) * 2
31659             });
31660             
31661             pos.push({
31662                 x : x + (this.unitWidth + this.gutter) * 1,
31663                 y : y
31664             });
31665             
31666             return pos;
31667             
31668         }
31669         
31670         pos.push({
31671             x : x,
31672             y : y
31673         });
31674
31675         pos.push({
31676             x : x + (this.unitWidth + this.gutter) * 2,
31677             y : y
31678         });
31679
31680         pos.push({
31681             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31682             y : y + (this.unitHeight + this.gutter) * 1
31683         });
31684
31685         pos.push({
31686             x : x + (this.unitWidth + this.gutter) * 2,
31687             y : y + (this.unitWidth + this.gutter) * 2
31688         });
31689
31690         return pos;
31691         
31692     },
31693     
31694     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31695     {
31696         var pos = [];
31697         
31698         if(box[0].size == 'md-left'){
31699             pos.push({
31700                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31701                 y : minY
31702             });
31703             
31704             return pos;
31705         }
31706         
31707         if(box[0].size == 'md-right'){
31708             pos.push({
31709                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31710                 y : minY + (this.unitWidth + this.gutter) * 1
31711             });
31712             
31713             return pos;
31714         }
31715         
31716         var rand = Math.floor(Math.random() * (4 - box[0].y));
31717         
31718         pos.push({
31719             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31720             y : minY + (this.unitWidth + this.gutter) * rand
31721         });
31722         
31723         return pos;
31724         
31725     },
31726     
31727     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31728     {
31729         var pos = [];
31730         
31731         if(box[0].size == 'xs'){
31732             
31733             pos.push({
31734                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31735                 y : minY
31736             });
31737
31738             pos.push({
31739                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31740                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31741             });
31742             
31743             return pos;
31744             
31745         }
31746         
31747         pos.push({
31748             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31749             y : minY
31750         });
31751
31752         pos.push({
31753             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31754             y : minY + (this.unitWidth + this.gutter) * 2
31755         });
31756         
31757         return pos;
31758         
31759     },
31760     
31761     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31762     {
31763         var pos = [];
31764         
31765         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31766             
31767             pos.push({
31768                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31769                 y : minY
31770             });
31771
31772             pos.push({
31773                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31774                 y : minY + (this.unitWidth + this.gutter) * 1
31775             });
31776             
31777             pos.push({
31778                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31779                 y : minY + (this.unitWidth + this.gutter) * 2
31780             });
31781             
31782             return pos;
31783             
31784         }
31785         
31786         if(box[0].size == 'xs' && box[1].size == 'xs'){
31787             
31788             pos.push({
31789                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31790                 y : minY
31791             });
31792
31793             pos.push({
31794                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31795                 y : minY
31796             });
31797             
31798             pos.push({
31799                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31800                 y : minY + (this.unitWidth + this.gutter) * 1
31801             });
31802             
31803             return pos;
31804             
31805         }
31806         
31807         pos.push({
31808             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31809             y : minY
31810         });
31811
31812         pos.push({
31813             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31814             y : minY + (this.unitWidth + this.gutter) * 2
31815         });
31816
31817         pos.push({
31818             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31819             y : minY + (this.unitWidth + this.gutter) * 2
31820         });
31821             
31822         return pos;
31823         
31824     },
31825     
31826     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31827     {
31828         var pos = [];
31829         
31830         if(box[0].size == 'xs'){
31831             
31832             pos.push({
31833                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31834                 y : minY
31835             });
31836
31837             pos.push({
31838                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31839                 y : minY
31840             });
31841             
31842             pos.push({
31843                 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),
31844                 y : minY
31845             });
31846             
31847             pos.push({
31848                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31849                 y : minY + (this.unitWidth + this.gutter) * 1
31850             });
31851             
31852             return pos;
31853             
31854         }
31855         
31856         pos.push({
31857             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31858             y : minY
31859         });
31860         
31861         pos.push({
31862             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31863             y : minY + (this.unitWidth + this.gutter) * 2
31864         });
31865         
31866         pos.push({
31867             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31868             y : minY + (this.unitWidth + this.gutter) * 2
31869         });
31870         
31871         pos.push({
31872             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),
31873             y : minY + (this.unitWidth + this.gutter) * 2
31874         });
31875
31876         return pos;
31877         
31878     },
31879     
31880     /**
31881     * remove a Masonry Brick
31882     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31883     */
31884     removeBrick : function(brick_id)
31885     {
31886         if (!brick_id) {
31887             return;
31888         }
31889         
31890         for (var i = 0; i<this.bricks.length; i++) {
31891             if (this.bricks[i].id == brick_id) {
31892                 this.bricks.splice(i,1);
31893                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31894                 this.initial();
31895             }
31896         }
31897     },
31898     
31899     /**
31900     * adds a Masonry Brick
31901     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31902     */
31903     addBrick : function(cfg)
31904     {
31905         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31906         //this.register(cn);
31907         cn.parentId = this.id;
31908         cn.onRender(this.el, null);
31909         return cn;
31910     },
31911     
31912     /**
31913     * register a Masonry Brick
31914     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31915     */
31916     
31917     register : function(brick)
31918     {
31919         this.bricks.push(brick);
31920         brick.masonryId = this.id;
31921     },
31922     
31923     /**
31924     * clear all the Masonry Brick
31925     */
31926     clearAll : function()
31927     {
31928         this.bricks = [];
31929         //this.getChildContainer().dom.innerHTML = "";
31930         this.el.dom.innerHTML = '';
31931     },
31932     
31933     getSelected : function()
31934     {
31935         if (!this.selectedBrick) {
31936             return false;
31937         }
31938         
31939         return this.selectedBrick;
31940     }
31941 });
31942
31943 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31944     
31945     groups: {},
31946      /**
31947     * register a Masonry Layout
31948     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31949     */
31950     
31951     register : function(layout)
31952     {
31953         this.groups[layout.id] = layout;
31954     },
31955     /**
31956     * fetch a  Masonry Layout based on the masonry layout ID
31957     * @param {string} the masonry layout to add
31958     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31959     */
31960     
31961     get: function(layout_id) {
31962         if (typeof(this.groups[layout_id]) == 'undefined') {
31963             return false;
31964         }
31965         return this.groups[layout_id] ;
31966     }
31967     
31968     
31969     
31970 });
31971
31972  
31973
31974  /**
31975  *
31976  * This is based on 
31977  * http://masonry.desandro.com
31978  *
31979  * The idea is to render all the bricks based on vertical width...
31980  *
31981  * The original code extends 'outlayer' - we might need to use that....
31982  * 
31983  */
31984
31985
31986 /**
31987  * @class Roo.bootstrap.LayoutMasonryAuto
31988  * @extends Roo.bootstrap.Component
31989  * Bootstrap Layout Masonry class
31990  * 
31991  * @constructor
31992  * Create a new Element
31993  * @param {Object} config The config object
31994  */
31995
31996 Roo.bootstrap.LayoutMasonryAuto = function(config){
31997     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31998 };
31999
32000 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32001     
32002       /**
32003      * @cfg {Boolean} isFitWidth  - resize the width..
32004      */   
32005     isFitWidth : false,  // options..
32006     /**
32007      * @cfg {Boolean} isOriginLeft = left align?
32008      */   
32009     isOriginLeft : true,
32010     /**
32011      * @cfg {Boolean} isOriginTop = top align?
32012      */   
32013     isOriginTop : false,
32014     /**
32015      * @cfg {Boolean} isLayoutInstant = no animation?
32016      */   
32017     isLayoutInstant : false, // needed?
32018     /**
32019      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32020      */   
32021     isResizingContainer : true,
32022     /**
32023      * @cfg {Number} columnWidth  width of the columns 
32024      */   
32025     
32026     columnWidth : 0,
32027     
32028     /**
32029      * @cfg {Number} maxCols maximum number of columns
32030      */   
32031     
32032     maxCols: 0,
32033     /**
32034      * @cfg {Number} padHeight padding below box..
32035      */   
32036     
32037     padHeight : 10, 
32038     
32039     /**
32040      * @cfg {Boolean} isAutoInitial defalut true
32041      */   
32042     
32043     isAutoInitial : true, 
32044     
32045     // private?
32046     gutter : 0,
32047     
32048     containerWidth: 0,
32049     initialColumnWidth : 0,
32050     currentSize : null,
32051     
32052     colYs : null, // array.
32053     maxY : 0,
32054     padWidth: 10,
32055     
32056     
32057     tag: 'div',
32058     cls: '',
32059     bricks: null, //CompositeElement
32060     cols : 0, // array?
32061     // element : null, // wrapped now this.el
32062     _isLayoutInited : null, 
32063     
32064     
32065     getAutoCreate : function(){
32066         
32067         var cfg = {
32068             tag: this.tag,
32069             cls: 'blog-masonary-wrapper ' + this.cls,
32070             cn : {
32071                 cls : 'mas-boxes masonary'
32072             }
32073         };
32074         
32075         return cfg;
32076     },
32077     
32078     getChildContainer: function( )
32079     {
32080         if (this.boxesEl) {
32081             return this.boxesEl;
32082         }
32083         
32084         this.boxesEl = this.el.select('.mas-boxes').first();
32085         
32086         return this.boxesEl;
32087     },
32088     
32089     
32090     initEvents : function()
32091     {
32092         var _this = this;
32093         
32094         if(this.isAutoInitial){
32095             Roo.log('hook children rendered');
32096             this.on('childrenrendered', function() {
32097                 Roo.log('children rendered');
32098                 _this.initial();
32099             } ,this);
32100         }
32101         
32102     },
32103     
32104     initial : function()
32105     {
32106         this.reloadItems();
32107
32108         this.currentSize = this.el.getBox(true);
32109
32110         /// was window resize... - let's see if this works..
32111         Roo.EventManager.onWindowResize(this.resize, this); 
32112
32113         if(!this.isAutoInitial){
32114             this.layout();
32115             return;
32116         }
32117         
32118         this.layout.defer(500,this);
32119     },
32120     
32121     reloadItems: function()
32122     {
32123         this.bricks = this.el.select('.masonry-brick', true);
32124         
32125         this.bricks.each(function(b) {
32126             //Roo.log(b.getSize());
32127             if (!b.attr('originalwidth')) {
32128                 b.attr('originalwidth',  b.getSize().width);
32129             }
32130             
32131         });
32132         
32133         Roo.log(this.bricks.elements.length);
32134     },
32135     
32136     resize : function()
32137     {
32138         Roo.log('resize');
32139         var cs = this.el.getBox(true);
32140         
32141         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32142             Roo.log("no change in with or X");
32143             return;
32144         }
32145         this.currentSize = cs;
32146         this.layout();
32147     },
32148     
32149     layout : function()
32150     {
32151          Roo.log('layout');
32152         this._resetLayout();
32153         //this._manageStamps();
32154       
32155         // don't animate first layout
32156         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32157         this.layoutItems( isInstant );
32158       
32159         // flag for initalized
32160         this._isLayoutInited = true;
32161     },
32162     
32163     layoutItems : function( isInstant )
32164     {
32165         //var items = this._getItemsForLayout( this.items );
32166         // original code supports filtering layout items.. we just ignore it..
32167         
32168         this._layoutItems( this.bricks , isInstant );
32169       
32170         this._postLayout();
32171     },
32172     _layoutItems : function ( items , isInstant)
32173     {
32174        //this.fireEvent( 'layout', this, items );
32175     
32176
32177         if ( !items || !items.elements.length ) {
32178           // no items, emit event with empty array
32179             return;
32180         }
32181
32182         var queue = [];
32183         items.each(function(item) {
32184             Roo.log("layout item");
32185             Roo.log(item);
32186             // get x/y object from method
32187             var position = this._getItemLayoutPosition( item );
32188             // enqueue
32189             position.item = item;
32190             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32191             queue.push( position );
32192         }, this);
32193       
32194         this._processLayoutQueue( queue );
32195     },
32196     /** Sets position of item in DOM
32197     * @param {Element} item
32198     * @param {Number} x - horizontal position
32199     * @param {Number} y - vertical position
32200     * @param {Boolean} isInstant - disables transitions
32201     */
32202     _processLayoutQueue : function( queue )
32203     {
32204         for ( var i=0, len = queue.length; i < len; i++ ) {
32205             var obj = queue[i];
32206             obj.item.position('absolute');
32207             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32208         }
32209     },
32210       
32211     
32212     /**
32213     * Any logic you want to do after each layout,
32214     * i.e. size the container
32215     */
32216     _postLayout : function()
32217     {
32218         this.resizeContainer();
32219     },
32220     
32221     resizeContainer : function()
32222     {
32223         if ( !this.isResizingContainer ) {
32224             return;
32225         }
32226         var size = this._getContainerSize();
32227         if ( size ) {
32228             this.el.setSize(size.width,size.height);
32229             this.boxesEl.setSize(size.width,size.height);
32230         }
32231     },
32232     
32233     
32234     
32235     _resetLayout : function()
32236     {
32237         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32238         this.colWidth = this.el.getWidth();
32239         //this.gutter = this.el.getWidth(); 
32240         
32241         this.measureColumns();
32242
32243         // reset column Y
32244         var i = this.cols;
32245         this.colYs = [];
32246         while (i--) {
32247             this.colYs.push( 0 );
32248         }
32249     
32250         this.maxY = 0;
32251     },
32252
32253     measureColumns : function()
32254     {
32255         this.getContainerWidth();
32256       // if columnWidth is 0, default to outerWidth of first item
32257         if ( !this.columnWidth ) {
32258             var firstItem = this.bricks.first();
32259             Roo.log(firstItem);
32260             this.columnWidth  = this.containerWidth;
32261             if (firstItem && firstItem.attr('originalwidth') ) {
32262                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32263             }
32264             // columnWidth fall back to item of first element
32265             Roo.log("set column width?");
32266                         this.initialColumnWidth = this.columnWidth  ;
32267
32268             // if first elem has no width, default to size of container
32269             
32270         }
32271         
32272         
32273         if (this.initialColumnWidth) {
32274             this.columnWidth = this.initialColumnWidth;
32275         }
32276         
32277         
32278             
32279         // column width is fixed at the top - however if container width get's smaller we should
32280         // reduce it...
32281         
32282         // this bit calcs how man columns..
32283             
32284         var columnWidth = this.columnWidth += this.gutter;
32285       
32286         // calculate columns
32287         var containerWidth = this.containerWidth + this.gutter;
32288         
32289         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32290         // fix rounding errors, typically with gutters
32291         var excess = columnWidth - containerWidth % columnWidth;
32292         
32293         
32294         // if overshoot is less than a pixel, round up, otherwise floor it
32295         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32296         cols = Math[ mathMethod ]( cols );
32297         this.cols = Math.max( cols, 1 );
32298         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32299         
32300          // padding positioning..
32301         var totalColWidth = this.cols * this.columnWidth;
32302         var padavail = this.containerWidth - totalColWidth;
32303         // so for 2 columns - we need 3 'pads'
32304         
32305         var padNeeded = (1+this.cols) * this.padWidth;
32306         
32307         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32308         
32309         this.columnWidth += padExtra
32310         //this.padWidth = Math.floor(padavail /  ( this.cols));
32311         
32312         // adjust colum width so that padding is fixed??
32313         
32314         // we have 3 columns ... total = width * 3
32315         // we have X left over... that should be used by 
32316         
32317         //if (this.expandC) {
32318             
32319         //}
32320         
32321         
32322         
32323     },
32324     
32325     getContainerWidth : function()
32326     {
32327        /* // container is parent if fit width
32328         var container = this.isFitWidth ? this.element.parentNode : this.element;
32329         // check that this.size and size are there
32330         // IE8 triggers resize on body size change, so they might not be
32331         
32332         var size = getSize( container );  //FIXME
32333         this.containerWidth = size && size.innerWidth; //FIXME
32334         */
32335          
32336         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32337         
32338     },
32339     
32340     _getItemLayoutPosition : function( item )  // what is item?
32341     {
32342         // we resize the item to our columnWidth..
32343       
32344         item.setWidth(this.columnWidth);
32345         item.autoBoxAdjust  = false;
32346         
32347         var sz = item.getSize();
32348  
32349         // how many columns does this brick span
32350         var remainder = this.containerWidth % this.columnWidth;
32351         
32352         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32353         // round if off by 1 pixel, otherwise use ceil
32354         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32355         colSpan = Math.min( colSpan, this.cols );
32356         
32357         // normally this should be '1' as we dont' currently allow multi width columns..
32358         
32359         var colGroup = this._getColGroup( colSpan );
32360         // get the minimum Y value from the columns
32361         var minimumY = Math.min.apply( Math, colGroup );
32362         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32363         
32364         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32365          
32366         // position the brick
32367         var position = {
32368             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32369             y: this.currentSize.y + minimumY + this.padHeight
32370         };
32371         
32372         Roo.log(position);
32373         // apply setHeight to necessary columns
32374         var setHeight = minimumY + sz.height + this.padHeight;
32375         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32376         
32377         var setSpan = this.cols + 1 - colGroup.length;
32378         for ( var i = 0; i < setSpan; i++ ) {
32379           this.colYs[ shortColIndex + i ] = setHeight ;
32380         }
32381       
32382         return position;
32383     },
32384     
32385     /**
32386      * @param {Number} colSpan - number of columns the element spans
32387      * @returns {Array} colGroup
32388      */
32389     _getColGroup : function( colSpan )
32390     {
32391         if ( colSpan < 2 ) {
32392           // if brick spans only one column, use all the column Ys
32393           return this.colYs;
32394         }
32395       
32396         var colGroup = [];
32397         // how many different places could this brick fit horizontally
32398         var groupCount = this.cols + 1 - colSpan;
32399         // for each group potential horizontal position
32400         for ( var i = 0; i < groupCount; i++ ) {
32401           // make an array of colY values for that one group
32402           var groupColYs = this.colYs.slice( i, i + colSpan );
32403           // and get the max value of the array
32404           colGroup[i] = Math.max.apply( Math, groupColYs );
32405         }
32406         return colGroup;
32407     },
32408     /*
32409     _manageStamp : function( stamp )
32410     {
32411         var stampSize =  stamp.getSize();
32412         var offset = stamp.getBox();
32413         // get the columns that this stamp affects
32414         var firstX = this.isOriginLeft ? offset.x : offset.right;
32415         var lastX = firstX + stampSize.width;
32416         var firstCol = Math.floor( firstX / this.columnWidth );
32417         firstCol = Math.max( 0, firstCol );
32418         
32419         var lastCol = Math.floor( lastX / this.columnWidth );
32420         // lastCol should not go over if multiple of columnWidth #425
32421         lastCol -= lastX % this.columnWidth ? 0 : 1;
32422         lastCol = Math.min( this.cols - 1, lastCol );
32423         
32424         // set colYs to bottom of the stamp
32425         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32426             stampSize.height;
32427             
32428         for ( var i = firstCol; i <= lastCol; i++ ) {
32429           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32430         }
32431     },
32432     */
32433     
32434     _getContainerSize : function()
32435     {
32436         this.maxY = Math.max.apply( Math, this.colYs );
32437         var size = {
32438             height: this.maxY
32439         };
32440       
32441         if ( this.isFitWidth ) {
32442             size.width = this._getContainerFitWidth();
32443         }
32444       
32445         return size;
32446     },
32447     
32448     _getContainerFitWidth : function()
32449     {
32450         var unusedCols = 0;
32451         // count unused columns
32452         var i = this.cols;
32453         while ( --i ) {
32454           if ( this.colYs[i] !== 0 ) {
32455             break;
32456           }
32457           unusedCols++;
32458         }
32459         // fit container to columns that have been used
32460         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32461     },
32462     
32463     needsResizeLayout : function()
32464     {
32465         var previousWidth = this.containerWidth;
32466         this.getContainerWidth();
32467         return previousWidth !== this.containerWidth;
32468     }
32469  
32470 });
32471
32472  
32473
32474  /*
32475  * - LGPL
32476  *
32477  * element
32478  * 
32479  */
32480
32481 /**
32482  * @class Roo.bootstrap.MasonryBrick
32483  * @extends Roo.bootstrap.Component
32484  * Bootstrap MasonryBrick class
32485  * 
32486  * @constructor
32487  * Create a new MasonryBrick
32488  * @param {Object} config The config object
32489  */
32490
32491 Roo.bootstrap.MasonryBrick = function(config){
32492     
32493     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32494     
32495     Roo.bootstrap.MasonryBrick.register(this);
32496     
32497     this.addEvents({
32498         // raw events
32499         /**
32500          * @event click
32501          * When a MasonryBrick is clcik
32502          * @param {Roo.bootstrap.MasonryBrick} this
32503          * @param {Roo.EventObject} e
32504          */
32505         "click" : true
32506     });
32507 };
32508
32509 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32510     
32511     /**
32512      * @cfg {String} title
32513      */   
32514     title : '',
32515     /**
32516      * @cfg {String} html
32517      */   
32518     html : '',
32519     /**
32520      * @cfg {String} bgimage
32521      */   
32522     bgimage : '',
32523     /**
32524      * @cfg {String} videourl
32525      */   
32526     videourl : '',
32527     /**
32528      * @cfg {String} cls
32529      */   
32530     cls : '',
32531     /**
32532      * @cfg {String} href
32533      */   
32534     href : '',
32535     /**
32536      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32537      */   
32538     size : 'xs',
32539     
32540     /**
32541      * @cfg {String} placetitle (center|bottom)
32542      */   
32543     placetitle : '',
32544     
32545     /**
32546      * @cfg {Boolean} isFitContainer defalut true
32547      */   
32548     isFitContainer : true, 
32549     
32550     /**
32551      * @cfg {Boolean} preventDefault defalut false
32552      */   
32553     preventDefault : false, 
32554     
32555     /**
32556      * @cfg {Boolean} inverse defalut false
32557      */   
32558     maskInverse : false, 
32559     
32560     getAutoCreate : function()
32561     {
32562         if(!this.isFitContainer){
32563             return this.getSplitAutoCreate();
32564         }
32565         
32566         var cls = 'masonry-brick masonry-brick-full';
32567         
32568         if(this.href.length){
32569             cls += ' masonry-brick-link';
32570         }
32571         
32572         if(this.bgimage.length){
32573             cls += ' masonry-brick-image';
32574         }
32575         
32576         if(this.maskInverse){
32577             cls += ' mask-inverse';
32578         }
32579         
32580         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32581             cls += ' enable-mask';
32582         }
32583         
32584         if(this.size){
32585             cls += ' masonry-' + this.size + '-brick';
32586         }
32587         
32588         if(this.placetitle.length){
32589             
32590             switch (this.placetitle) {
32591                 case 'center' :
32592                     cls += ' masonry-center-title';
32593                     break;
32594                 case 'bottom' :
32595                     cls += ' masonry-bottom-title';
32596                     break;
32597                 default:
32598                     break;
32599             }
32600             
32601         } else {
32602             if(!this.html.length && !this.bgimage.length){
32603                 cls += ' masonry-center-title';
32604             }
32605
32606             if(!this.html.length && this.bgimage.length){
32607                 cls += ' masonry-bottom-title';
32608             }
32609         }
32610         
32611         if(this.cls){
32612             cls += ' ' + this.cls;
32613         }
32614         
32615         var cfg = {
32616             tag: (this.href.length) ? 'a' : 'div',
32617             cls: cls,
32618             cn: [
32619                 {
32620                     tag: 'div',
32621                     cls: 'masonry-brick-mask'
32622                 },
32623                 {
32624                     tag: 'div',
32625                     cls: 'masonry-brick-paragraph',
32626                     cn: []
32627                 }
32628             ]
32629         };
32630         
32631         if(this.href.length){
32632             cfg.href = this.href;
32633         }
32634         
32635         var cn = cfg.cn[1].cn;
32636         
32637         if(this.title.length){
32638             cn.push({
32639                 tag: 'h4',
32640                 cls: 'masonry-brick-title',
32641                 html: this.title
32642             });
32643         }
32644         
32645         if(this.html.length){
32646             cn.push({
32647                 tag: 'p',
32648                 cls: 'masonry-brick-text',
32649                 html: this.html
32650             });
32651         }
32652         
32653         if (!this.title.length && !this.html.length) {
32654             cfg.cn[1].cls += ' hide';
32655         }
32656         
32657         if(this.bgimage.length){
32658             cfg.cn.push({
32659                 tag: 'img',
32660                 cls: 'masonry-brick-image-view',
32661                 src: this.bgimage
32662             });
32663         }
32664         
32665         if(this.videourl.length){
32666             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32667             // youtube support only?
32668             cfg.cn.push({
32669                 tag: 'iframe',
32670                 cls: 'masonry-brick-image-view',
32671                 src: vurl,
32672                 frameborder : 0,
32673                 allowfullscreen : true
32674             });
32675         }
32676         
32677         return cfg;
32678         
32679     },
32680     
32681     getSplitAutoCreate : function()
32682     {
32683         var cls = 'masonry-brick masonry-brick-split';
32684         
32685         if(this.href.length){
32686             cls += ' masonry-brick-link';
32687         }
32688         
32689         if(this.bgimage.length){
32690             cls += ' masonry-brick-image';
32691         }
32692         
32693         if(this.size){
32694             cls += ' masonry-' + this.size + '-brick';
32695         }
32696         
32697         switch (this.placetitle) {
32698             case 'center' :
32699                 cls += ' masonry-center-title';
32700                 break;
32701             case 'bottom' :
32702                 cls += ' masonry-bottom-title';
32703                 break;
32704             default:
32705                 if(!this.bgimage.length){
32706                     cls += ' masonry-center-title';
32707                 }
32708
32709                 if(this.bgimage.length){
32710                     cls += ' masonry-bottom-title';
32711                 }
32712                 break;
32713         }
32714         
32715         if(this.cls){
32716             cls += ' ' + this.cls;
32717         }
32718         
32719         var cfg = {
32720             tag: (this.href.length) ? 'a' : 'div',
32721             cls: cls,
32722             cn: [
32723                 {
32724                     tag: 'div',
32725                     cls: 'masonry-brick-split-head',
32726                     cn: [
32727                         {
32728                             tag: 'div',
32729                             cls: 'masonry-brick-paragraph',
32730                             cn: []
32731                         }
32732                     ]
32733                 },
32734                 {
32735                     tag: 'div',
32736                     cls: 'masonry-brick-split-body',
32737                     cn: []
32738                 }
32739             ]
32740         };
32741         
32742         if(this.href.length){
32743             cfg.href = this.href;
32744         }
32745         
32746         if(this.title.length){
32747             cfg.cn[0].cn[0].cn.push({
32748                 tag: 'h4',
32749                 cls: 'masonry-brick-title',
32750                 html: this.title
32751             });
32752         }
32753         
32754         if(this.html.length){
32755             cfg.cn[1].cn.push({
32756                 tag: 'p',
32757                 cls: 'masonry-brick-text',
32758                 html: this.html
32759             });
32760         }
32761
32762         if(this.bgimage.length){
32763             cfg.cn[0].cn.push({
32764                 tag: 'img',
32765                 cls: 'masonry-brick-image-view',
32766                 src: this.bgimage
32767             });
32768         }
32769         
32770         if(this.videourl.length){
32771             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32772             // youtube support only?
32773             cfg.cn[0].cn.cn.push({
32774                 tag: 'iframe',
32775                 cls: 'masonry-brick-image-view',
32776                 src: vurl,
32777                 frameborder : 0,
32778                 allowfullscreen : true
32779             });
32780         }
32781         
32782         return cfg;
32783     },
32784     
32785     initEvents: function() 
32786     {
32787         switch (this.size) {
32788             case 'xs' :
32789                 this.x = 1;
32790                 this.y = 1;
32791                 break;
32792             case 'sm' :
32793                 this.x = 2;
32794                 this.y = 2;
32795                 break;
32796             case 'md' :
32797             case 'md-left' :
32798             case 'md-right' :
32799                 this.x = 3;
32800                 this.y = 3;
32801                 break;
32802             case 'tall' :
32803                 this.x = 2;
32804                 this.y = 3;
32805                 break;
32806             case 'wide' :
32807                 this.x = 3;
32808                 this.y = 2;
32809                 break;
32810             case 'wide-thin' :
32811                 this.x = 3;
32812                 this.y = 1;
32813                 break;
32814                         
32815             default :
32816                 break;
32817         }
32818         
32819         if(Roo.isTouch){
32820             this.el.on('touchstart', this.onTouchStart, this);
32821             this.el.on('touchmove', this.onTouchMove, this);
32822             this.el.on('touchend', this.onTouchEnd, this);
32823             this.el.on('contextmenu', this.onContextMenu, this);
32824         } else {
32825             this.el.on('mouseenter'  ,this.enter, this);
32826             this.el.on('mouseleave', this.leave, this);
32827             this.el.on('click', this.onClick, this);
32828         }
32829         
32830         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32831             this.parent().bricks.push(this);   
32832         }
32833         
32834     },
32835     
32836     onClick: function(e, el)
32837     {
32838         var time = this.endTimer - this.startTimer;
32839         // Roo.log(e.preventDefault());
32840         if(Roo.isTouch){
32841             if(time > 1000){
32842                 e.preventDefault();
32843                 return;
32844             }
32845         }
32846         
32847         if(!this.preventDefault){
32848             return;
32849         }
32850         
32851         e.preventDefault();
32852         
32853         if (this.activeClass != '') {
32854             this.selectBrick();
32855         }
32856         
32857         this.fireEvent('click', this, e);
32858     },
32859     
32860     enter: function(e, el)
32861     {
32862         e.preventDefault();
32863         
32864         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32865             return;
32866         }
32867         
32868         if(this.bgimage.length && this.html.length){
32869             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32870         }
32871     },
32872     
32873     leave: function(e, el)
32874     {
32875         e.preventDefault();
32876         
32877         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32878             return;
32879         }
32880         
32881         if(this.bgimage.length && this.html.length){
32882             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32883         }
32884     },
32885     
32886     onTouchStart: function(e, el)
32887     {
32888 //        e.preventDefault();
32889         
32890         this.touchmoved = false;
32891         
32892         if(!this.isFitContainer){
32893             return;
32894         }
32895         
32896         if(!this.bgimage.length || !this.html.length){
32897             return;
32898         }
32899         
32900         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32901         
32902         this.timer = new Date().getTime();
32903         
32904     },
32905     
32906     onTouchMove: function(e, el)
32907     {
32908         this.touchmoved = true;
32909     },
32910     
32911     onContextMenu : function(e,el)
32912     {
32913         e.preventDefault();
32914         e.stopPropagation();
32915         return false;
32916     },
32917     
32918     onTouchEnd: function(e, el)
32919     {
32920 //        e.preventDefault();
32921         
32922         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32923         
32924             this.leave(e,el);
32925             
32926             return;
32927         }
32928         
32929         if(!this.bgimage.length || !this.html.length){
32930             
32931             if(this.href.length){
32932                 window.location.href = this.href;
32933             }
32934             
32935             return;
32936         }
32937         
32938         if(!this.isFitContainer){
32939             return;
32940         }
32941         
32942         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32943         
32944         window.location.href = this.href;
32945     },
32946     
32947     //selection on single brick only
32948     selectBrick : function() {
32949         
32950         if (!this.parentId) {
32951             return;
32952         }
32953         
32954         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32955         var index = m.selectedBrick.indexOf(this.id);
32956         
32957         if ( index > -1) {
32958             m.selectedBrick.splice(index,1);
32959             this.el.removeClass(this.activeClass);
32960             return;
32961         }
32962         
32963         for(var i = 0; i < m.selectedBrick.length; i++) {
32964             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32965             b.el.removeClass(b.activeClass);
32966         }
32967         
32968         m.selectedBrick = [];
32969         
32970         m.selectedBrick.push(this.id);
32971         this.el.addClass(this.activeClass);
32972         return;
32973     },
32974     
32975     isSelected : function(){
32976         return this.el.hasClass(this.activeClass);
32977         
32978     }
32979 });
32980
32981 Roo.apply(Roo.bootstrap.MasonryBrick, {
32982     
32983     //groups: {},
32984     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32985      /**
32986     * register a Masonry Brick
32987     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32988     */
32989     
32990     register : function(brick)
32991     {
32992         //this.groups[brick.id] = brick;
32993         this.groups.add(brick.id, brick);
32994     },
32995     /**
32996     * fetch a  masonry brick based on the masonry brick ID
32997     * @param {string} the masonry brick to add
32998     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32999     */
33000     
33001     get: function(brick_id) 
33002     {
33003         // if (typeof(this.groups[brick_id]) == 'undefined') {
33004         //     return false;
33005         // }
33006         // return this.groups[brick_id] ;
33007         
33008         if(this.groups.key(brick_id)) {
33009             return this.groups.key(brick_id);
33010         }
33011         
33012         return false;
33013     }
33014     
33015     
33016     
33017 });
33018
33019  /*
33020  * - LGPL
33021  *
33022  * element
33023  * 
33024  */
33025
33026 /**
33027  * @class Roo.bootstrap.Brick
33028  * @extends Roo.bootstrap.Component
33029  * Bootstrap Brick class
33030  * 
33031  * @constructor
33032  * Create a new Brick
33033  * @param {Object} config The config object
33034  */
33035
33036 Roo.bootstrap.Brick = function(config){
33037     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33038     
33039     this.addEvents({
33040         // raw events
33041         /**
33042          * @event click
33043          * When a Brick is click
33044          * @param {Roo.bootstrap.Brick} this
33045          * @param {Roo.EventObject} e
33046          */
33047         "click" : true
33048     });
33049 };
33050
33051 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33052     
33053     /**
33054      * @cfg {String} title
33055      */   
33056     title : '',
33057     /**
33058      * @cfg {String} html
33059      */   
33060     html : '',
33061     /**
33062      * @cfg {String} bgimage
33063      */   
33064     bgimage : '',
33065     /**
33066      * @cfg {String} cls
33067      */   
33068     cls : '',
33069     /**
33070      * @cfg {String} href
33071      */   
33072     href : '',
33073     /**
33074      * @cfg {String} video
33075      */   
33076     video : '',
33077     /**
33078      * @cfg {Boolean} square
33079      */   
33080     square : true,
33081     
33082     getAutoCreate : function()
33083     {
33084         var cls = 'roo-brick';
33085         
33086         if(this.href.length){
33087             cls += ' roo-brick-link';
33088         }
33089         
33090         if(this.bgimage.length){
33091             cls += ' roo-brick-image';
33092         }
33093         
33094         if(!this.html.length && !this.bgimage.length){
33095             cls += ' roo-brick-center-title';
33096         }
33097         
33098         if(!this.html.length && this.bgimage.length){
33099             cls += ' roo-brick-bottom-title';
33100         }
33101         
33102         if(this.cls){
33103             cls += ' ' + this.cls;
33104         }
33105         
33106         var cfg = {
33107             tag: (this.href.length) ? 'a' : 'div',
33108             cls: cls,
33109             cn: [
33110                 {
33111                     tag: 'div',
33112                     cls: 'roo-brick-paragraph',
33113                     cn: []
33114                 }
33115             ]
33116         };
33117         
33118         if(this.href.length){
33119             cfg.href = this.href;
33120         }
33121         
33122         var cn = cfg.cn[0].cn;
33123         
33124         if(this.title.length){
33125             cn.push({
33126                 tag: 'h4',
33127                 cls: 'roo-brick-title',
33128                 html: this.title
33129             });
33130         }
33131         
33132         if(this.html.length){
33133             cn.push({
33134                 tag: 'p',
33135                 cls: 'roo-brick-text',
33136                 html: this.html
33137             });
33138         } else {
33139             cn.cls += ' hide';
33140         }
33141         
33142         if(this.bgimage.length){
33143             cfg.cn.push({
33144                 tag: 'img',
33145                 cls: 'roo-brick-image-view',
33146                 src: this.bgimage
33147             });
33148         }
33149         
33150         return cfg;
33151     },
33152     
33153     initEvents: function() 
33154     {
33155         if(this.title.length || this.html.length){
33156             this.el.on('mouseenter'  ,this.enter, this);
33157             this.el.on('mouseleave', this.leave, this);
33158         }
33159         
33160         Roo.EventManager.onWindowResize(this.resize, this); 
33161         
33162         if(this.bgimage.length){
33163             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33164             this.imageEl.on('load', this.onImageLoad, this);
33165             return;
33166         }
33167         
33168         this.resize();
33169     },
33170     
33171     onImageLoad : function()
33172     {
33173         this.resize();
33174     },
33175     
33176     resize : function()
33177     {
33178         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33179         
33180         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33181         
33182         if(this.bgimage.length){
33183             var image = this.el.select('.roo-brick-image-view', true).first();
33184             
33185             image.setWidth(paragraph.getWidth());
33186             
33187             if(this.square){
33188                 image.setHeight(paragraph.getWidth());
33189             }
33190             
33191             this.el.setHeight(image.getHeight());
33192             paragraph.setHeight(image.getHeight());
33193             
33194         }
33195         
33196     },
33197     
33198     enter: function(e, el)
33199     {
33200         e.preventDefault();
33201         
33202         if(this.bgimage.length){
33203             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33204             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33205         }
33206     },
33207     
33208     leave: function(e, el)
33209     {
33210         e.preventDefault();
33211         
33212         if(this.bgimage.length){
33213             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33214             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33215         }
33216     }
33217     
33218 });
33219
33220  
33221
33222  /*
33223  * - LGPL
33224  *
33225  * Number field 
33226  */
33227
33228 /**
33229  * @class Roo.bootstrap.NumberField
33230  * @extends Roo.bootstrap.Input
33231  * Bootstrap NumberField class
33232  * 
33233  * 
33234  * 
33235  * 
33236  * @constructor
33237  * Create a new NumberField
33238  * @param {Object} config The config object
33239  */
33240
33241 Roo.bootstrap.NumberField = function(config){
33242     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33243 };
33244
33245 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33246     
33247     /**
33248      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33249      */
33250     allowDecimals : true,
33251     /**
33252      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33253      */
33254     decimalSeparator : ".",
33255     /**
33256      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33257      */
33258     decimalPrecision : 2,
33259     /**
33260      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33261      */
33262     allowNegative : true,
33263     
33264     /**
33265      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33266      */
33267     allowZero: true,
33268     /**
33269      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33270      */
33271     minValue : Number.NEGATIVE_INFINITY,
33272     /**
33273      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33274      */
33275     maxValue : Number.MAX_VALUE,
33276     /**
33277      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33278      */
33279     minText : "The minimum value for this field is {0}",
33280     /**
33281      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33282      */
33283     maxText : "The maximum value for this field is {0}",
33284     /**
33285      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33286      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33287      */
33288     nanText : "{0} is not a valid number",
33289     /**
33290      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33291      */
33292     thousandsDelimiter : false,
33293     /**
33294      * @cfg {String} valueAlign alignment of value
33295      */
33296     valueAlign : "left",
33297
33298     getAutoCreate : function()
33299     {
33300         var hiddenInput = {
33301             tag: 'input',
33302             type: 'hidden',
33303             id: Roo.id(),
33304             cls: 'hidden-number-input'
33305         };
33306         
33307         if (this.name) {
33308             hiddenInput.name = this.name;
33309         }
33310         
33311         this.name = '';
33312         
33313         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33314         
33315         this.name = hiddenInput.name;
33316         
33317         if(cfg.cn.length > 0) {
33318             cfg.cn.push(hiddenInput);
33319         }
33320         
33321         return cfg;
33322     },
33323
33324     // private
33325     initEvents : function()
33326     {   
33327         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33328         
33329         var allowed = "0123456789";
33330         
33331         if(this.allowDecimals){
33332             allowed += this.decimalSeparator;
33333         }
33334         
33335         if(this.allowNegative){
33336             allowed += "-";
33337         }
33338         
33339         if(this.thousandsDelimiter) {
33340             allowed += ",";
33341         }
33342         
33343         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33344         
33345         var keyPress = function(e){
33346             
33347             var k = e.getKey();
33348             
33349             var c = e.getCharCode();
33350             
33351             if(
33352                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33353                     allowed.indexOf(String.fromCharCode(c)) === -1
33354             ){
33355                 e.stopEvent();
33356                 return;
33357             }
33358             
33359             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33360                 return;
33361             }
33362             
33363             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33364                 e.stopEvent();
33365             }
33366         };
33367         
33368         this.el.on("keypress", keyPress, this);
33369     },
33370     
33371     validateValue : function(value)
33372     {
33373         
33374         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33375             return false;
33376         }
33377         
33378         var num = this.parseValue(value);
33379         
33380         if(isNaN(num)){
33381             this.markInvalid(String.format(this.nanText, value));
33382             return false;
33383         }
33384         
33385         if(num < this.minValue){
33386             this.markInvalid(String.format(this.minText, this.minValue));
33387             return false;
33388         }
33389         
33390         if(num > this.maxValue){
33391             this.markInvalid(String.format(this.maxText, this.maxValue));
33392             return false;
33393         }
33394         
33395         return true;
33396     },
33397
33398     getValue : function()
33399     {
33400         var v = this.hiddenEl().getValue();
33401         
33402         return this.fixPrecision(this.parseValue(v));
33403     },
33404
33405     parseValue : function(value)
33406     {
33407         if(this.thousandsDelimiter) {
33408             value += "";
33409             r = new RegExp(",", "g");
33410             value = value.replace(r, "");
33411         }
33412         
33413         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33414         return isNaN(value) ? '' : value;
33415     },
33416
33417     fixPrecision : function(value)
33418     {
33419         if(this.thousandsDelimiter) {
33420             value += "";
33421             r = new RegExp(",", "g");
33422             value = value.replace(r, "");
33423         }
33424         
33425         var nan = isNaN(value);
33426         
33427         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33428             return nan ? '' : value;
33429         }
33430         return parseFloat(value).toFixed(this.decimalPrecision);
33431     },
33432
33433     setValue : function(v)
33434     {
33435         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33436         
33437         this.value = v;
33438         
33439         if(this.rendered){
33440             
33441             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33442             
33443             this.inputEl().dom.value = (v == '') ? '' :
33444                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33445             
33446             if(!this.allowZero && v === '0') {
33447                 this.hiddenEl().dom.value = '';
33448                 this.inputEl().dom.value = '';
33449             }
33450             
33451             this.validate();
33452         }
33453     },
33454
33455     decimalPrecisionFcn : function(v)
33456     {
33457         return Math.floor(v);
33458     },
33459
33460     beforeBlur : function()
33461     {
33462         var v = this.parseValue(this.getRawValue());
33463         
33464         if(v || v === 0 || v === ''){
33465             this.setValue(v);
33466         }
33467     },
33468     
33469     hiddenEl : function()
33470     {
33471         return this.el.select('input.hidden-number-input',true).first();
33472     }
33473     
33474 });
33475
33476  
33477
33478 /*
33479 * Licence: LGPL
33480 */
33481
33482 /**
33483  * @class Roo.bootstrap.DocumentSlider
33484  * @extends Roo.bootstrap.Component
33485  * Bootstrap DocumentSlider class
33486  * 
33487  * @constructor
33488  * Create a new DocumentViewer
33489  * @param {Object} config The config object
33490  */
33491
33492 Roo.bootstrap.DocumentSlider = function(config){
33493     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33494     
33495     this.files = [];
33496     
33497     this.addEvents({
33498         /**
33499          * @event initial
33500          * Fire after initEvent
33501          * @param {Roo.bootstrap.DocumentSlider} this
33502          */
33503         "initial" : true,
33504         /**
33505          * @event update
33506          * Fire after update
33507          * @param {Roo.bootstrap.DocumentSlider} this
33508          */
33509         "update" : true,
33510         /**
33511          * @event click
33512          * Fire after click
33513          * @param {Roo.bootstrap.DocumentSlider} this
33514          */
33515         "click" : true
33516     });
33517 };
33518
33519 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33520     
33521     files : false,
33522     
33523     indicator : 0,
33524     
33525     getAutoCreate : function()
33526     {
33527         var cfg = {
33528             tag : 'div',
33529             cls : 'roo-document-slider',
33530             cn : [
33531                 {
33532                     tag : 'div',
33533                     cls : 'roo-document-slider-header',
33534                     cn : [
33535                         {
33536                             tag : 'div',
33537                             cls : 'roo-document-slider-header-title'
33538                         }
33539                     ]
33540                 },
33541                 {
33542                     tag : 'div',
33543                     cls : 'roo-document-slider-body',
33544                     cn : [
33545                         {
33546                             tag : 'div',
33547                             cls : 'roo-document-slider-prev',
33548                             cn : [
33549                                 {
33550                                     tag : 'i',
33551                                     cls : 'fa fa-chevron-left'
33552                                 }
33553                             ]
33554                         },
33555                         {
33556                             tag : 'div',
33557                             cls : 'roo-document-slider-thumb',
33558                             cn : [
33559                                 {
33560                                     tag : 'img',
33561                                     cls : 'roo-document-slider-image'
33562                                 }
33563                             ]
33564                         },
33565                         {
33566                             tag : 'div',
33567                             cls : 'roo-document-slider-next',
33568                             cn : [
33569                                 {
33570                                     tag : 'i',
33571                                     cls : 'fa fa-chevron-right'
33572                                 }
33573                             ]
33574                         }
33575                     ]
33576                 }
33577             ]
33578         };
33579         
33580         return cfg;
33581     },
33582     
33583     initEvents : function()
33584     {
33585         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33586         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33587         
33588         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33589         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33590         
33591         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33592         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33593         
33594         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33595         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33596         
33597         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33598         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33599         
33600         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33601         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33602         
33603         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33604         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33605         
33606         this.thumbEl.on('click', this.onClick, this);
33607         
33608         this.prevIndicator.on('click', this.prev, this);
33609         
33610         this.nextIndicator.on('click', this.next, this);
33611         
33612     },
33613     
33614     initial : function()
33615     {
33616         if(this.files.length){
33617             this.indicator = 1;
33618             this.update()
33619         }
33620         
33621         this.fireEvent('initial', this);
33622     },
33623     
33624     update : function()
33625     {
33626         this.imageEl.attr('src', this.files[this.indicator - 1]);
33627         
33628         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33629         
33630         this.prevIndicator.show();
33631         
33632         if(this.indicator == 1){
33633             this.prevIndicator.hide();
33634         }
33635         
33636         this.nextIndicator.show();
33637         
33638         if(this.indicator == this.files.length){
33639             this.nextIndicator.hide();
33640         }
33641         
33642         this.thumbEl.scrollTo('top');
33643         
33644         this.fireEvent('update', this);
33645     },
33646     
33647     onClick : function(e)
33648     {
33649         e.preventDefault();
33650         
33651         this.fireEvent('click', this);
33652     },
33653     
33654     prev : function(e)
33655     {
33656         e.preventDefault();
33657         
33658         this.indicator = Math.max(1, this.indicator - 1);
33659         
33660         this.update();
33661     },
33662     
33663     next : function(e)
33664     {
33665         e.preventDefault();
33666         
33667         this.indicator = Math.min(this.files.length, this.indicator + 1);
33668         
33669         this.update();
33670     }
33671 });
33672 /*
33673  * - LGPL
33674  *
33675  * RadioSet
33676  *
33677  *
33678  */
33679
33680 /**
33681  * @class Roo.bootstrap.RadioSet
33682  * @extends Roo.bootstrap.Input
33683  * Bootstrap RadioSet class
33684  * @cfg {String} indicatorpos (left|right) default left
33685  * @cfg {Boolean} inline (true|false) inline the element (default true)
33686  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33687  * @constructor
33688  * Create a new RadioSet
33689  * @param {Object} config The config object
33690  */
33691
33692 Roo.bootstrap.RadioSet = function(config){
33693     
33694     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33695     
33696     this.radioes = [];
33697     
33698     Roo.bootstrap.RadioSet.register(this);
33699     
33700     this.addEvents({
33701         /**
33702         * @event check
33703         * Fires when the element is checked or unchecked.
33704         * @param {Roo.bootstrap.RadioSet} this This radio
33705         * @param {Roo.bootstrap.Radio} item The checked item
33706         */
33707        check : true,
33708        /**
33709         * @event click
33710         * Fires when the element is click.
33711         * @param {Roo.bootstrap.RadioSet} this This radio set
33712         * @param {Roo.bootstrap.Radio} item The checked item
33713         * @param {Roo.EventObject} e The event object
33714         */
33715        click : true
33716     });
33717     
33718 };
33719
33720 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33721
33722     radioes : false,
33723     
33724     inline : true,
33725     
33726     weight : '',
33727     
33728     indicatorpos : 'left',
33729     
33730     getAutoCreate : function()
33731     {
33732         var label = {
33733             tag : 'label',
33734             cls : 'roo-radio-set-label',
33735             cn : [
33736                 {
33737                     tag : 'span',
33738                     html : this.fieldLabel
33739                 }
33740             ]
33741         };
33742         
33743         if(this.indicatorpos == 'left'){
33744             label.cn.unshift({
33745                 tag : 'i',
33746                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33747                 tooltip : 'This field is required'
33748             });
33749         } else {
33750             label.cn.push({
33751                 tag : 'i',
33752                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33753                 tooltip : 'This field is required'
33754             });
33755         }
33756         
33757         var items = {
33758             tag : 'div',
33759             cls : 'roo-radio-set-items'
33760         };
33761         
33762         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33763         
33764         if (align === 'left' && this.fieldLabel.length) {
33765             
33766             items = {
33767                 cls : "roo-radio-set-right", 
33768                 cn: [
33769                     items
33770                 ]
33771             };
33772             
33773             if(this.labelWidth > 12){
33774                 label.style = "width: " + this.labelWidth + 'px';
33775             }
33776             
33777             if(this.labelWidth < 13 && this.labelmd == 0){
33778                 this.labelmd = this.labelWidth;
33779             }
33780             
33781             if(this.labellg > 0){
33782                 label.cls += ' col-lg-' + this.labellg;
33783                 items.cls += ' col-lg-' + (12 - this.labellg);
33784             }
33785             
33786             if(this.labelmd > 0){
33787                 label.cls += ' col-md-' + this.labelmd;
33788                 items.cls += ' col-md-' + (12 - this.labelmd);
33789             }
33790             
33791             if(this.labelsm > 0){
33792                 label.cls += ' col-sm-' + this.labelsm;
33793                 items.cls += ' col-sm-' + (12 - this.labelsm);
33794             }
33795             
33796             if(this.labelxs > 0){
33797                 label.cls += ' col-xs-' + this.labelxs;
33798                 items.cls += ' col-xs-' + (12 - this.labelxs);
33799             }
33800         }
33801         
33802         var cfg = {
33803             tag : 'div',
33804             cls : 'roo-radio-set',
33805             cn : [
33806                 {
33807                     tag : 'input',
33808                     cls : 'roo-radio-set-input',
33809                     type : 'hidden',
33810                     name : this.name,
33811                     value : this.value ? this.value :  ''
33812                 },
33813                 label,
33814                 items
33815             ]
33816         };
33817         
33818         if(this.weight.length){
33819             cfg.cls += ' roo-radio-' + this.weight;
33820         }
33821         
33822         if(this.inline) {
33823             cfg.cls += ' roo-radio-set-inline';
33824         }
33825         
33826         var settings=this;
33827         ['xs','sm','md','lg'].map(function(size){
33828             if (settings[size]) {
33829                 cfg.cls += ' col-' + size + '-' + settings[size];
33830             }
33831         });
33832         
33833         return cfg;
33834         
33835     },
33836
33837     initEvents : function()
33838     {
33839         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33840         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33841         
33842         if(!this.fieldLabel.length){
33843             this.labelEl.hide();
33844         }
33845         
33846         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33847         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33848         
33849         this.indicator = this.indicatorEl();
33850         
33851         if(this.indicator){
33852             this.indicator.addClass('invisible');
33853         }
33854         
33855         this.originalValue = this.getValue();
33856         
33857     },
33858     
33859     inputEl: function ()
33860     {
33861         return this.el.select('.roo-radio-set-input', true).first();
33862     },
33863     
33864     getChildContainer : function()
33865     {
33866         return this.itemsEl;
33867     },
33868     
33869     register : function(item)
33870     {
33871         this.radioes.push(item);
33872         
33873     },
33874     
33875     validate : function()
33876     {   
33877         if(this.getVisibilityEl().hasClass('hidden')){
33878             return true;
33879         }
33880         
33881         var valid = false;
33882         
33883         Roo.each(this.radioes, function(i){
33884             if(!i.checked){
33885                 return;
33886             }
33887             
33888             valid = true;
33889             return false;
33890         });
33891         
33892         if(this.allowBlank) {
33893             return true;
33894         }
33895         
33896         if(this.disabled || valid){
33897             this.markValid();
33898             return true;
33899         }
33900         
33901         this.markInvalid();
33902         return false;
33903         
33904     },
33905     
33906     markValid : function()
33907     {
33908         if(this.labelEl.isVisible(true)){
33909             this.indicatorEl().removeClass('visible');
33910             this.indicatorEl().addClass('invisible');
33911         }
33912         
33913         this.el.removeClass([this.invalidClass, this.validClass]);
33914         this.el.addClass(this.validClass);
33915         
33916         this.fireEvent('valid', this);
33917     },
33918     
33919     markInvalid : function(msg)
33920     {
33921         if(this.allowBlank || this.disabled){
33922             return;
33923         }
33924         
33925         if(this.labelEl.isVisible(true)){
33926             this.indicatorEl().removeClass('invisible');
33927             this.indicatorEl().addClass('visible');
33928         }
33929         
33930         this.el.removeClass([this.invalidClass, this.validClass]);
33931         this.el.addClass(this.invalidClass);
33932         
33933         this.fireEvent('invalid', this, msg);
33934         
33935     },
33936     
33937     setValue : function(v, suppressEvent)
33938     {   
33939         if(this.value === v){
33940             return;
33941         }
33942         
33943         this.value = v;
33944         
33945         if(this.rendered){
33946             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33947         }
33948         
33949         Roo.each(this.radioes, function(i){
33950             i.checked = false;
33951             i.el.removeClass('checked');
33952         });
33953         
33954         Roo.each(this.radioes, function(i){
33955             
33956             if(i.value === v || i.value.toString() === v.toString()){
33957                 i.checked = true;
33958                 i.el.addClass('checked');
33959                 
33960                 if(suppressEvent !== true){
33961                     this.fireEvent('check', this, i);
33962                 }
33963                 
33964                 return false;
33965             }
33966             
33967         }, this);
33968         
33969         this.validate();
33970     },
33971     
33972     clearInvalid : function(){
33973         
33974         if(!this.el || this.preventMark){
33975             return;
33976         }
33977         
33978         this.el.removeClass([this.invalidClass]);
33979         
33980         this.fireEvent('valid', this);
33981     }
33982     
33983 });
33984
33985 Roo.apply(Roo.bootstrap.RadioSet, {
33986     
33987     groups: {},
33988     
33989     register : function(set)
33990     {
33991         this.groups[set.name] = set;
33992     },
33993     
33994     get: function(name) 
33995     {
33996         if (typeof(this.groups[name]) == 'undefined') {
33997             return false;
33998         }
33999         
34000         return this.groups[name] ;
34001     }
34002     
34003 });
34004 /*
34005  * Based on:
34006  * Ext JS Library 1.1.1
34007  * Copyright(c) 2006-2007, Ext JS, LLC.
34008  *
34009  * Originally Released Under LGPL - original licence link has changed is not relivant.
34010  *
34011  * Fork - LGPL
34012  * <script type="text/javascript">
34013  */
34014
34015
34016 /**
34017  * @class Roo.bootstrap.SplitBar
34018  * @extends Roo.util.Observable
34019  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34020  * <br><br>
34021  * Usage:
34022  * <pre><code>
34023 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34024                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34025 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34026 split.minSize = 100;
34027 split.maxSize = 600;
34028 split.animate = true;
34029 split.on('moved', splitterMoved);
34030 </code></pre>
34031  * @constructor
34032  * Create a new SplitBar
34033  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34034  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34035  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34036  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34037                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34038                         position of the SplitBar).
34039  */
34040 Roo.bootstrap.SplitBar = function(cfg){
34041     
34042     /** @private */
34043     
34044     //{
34045     //  dragElement : elm
34046     //  resizingElement: el,
34047         // optional..
34048     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34049     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34050         // existingProxy ???
34051     //}
34052     
34053     this.el = Roo.get(cfg.dragElement, true);
34054     this.el.dom.unselectable = "on";
34055     /** @private */
34056     this.resizingEl = Roo.get(cfg.resizingElement, true);
34057
34058     /**
34059      * @private
34060      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34061      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34062      * @type Number
34063      */
34064     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34065     
34066     /**
34067      * The minimum size of the resizing element. (Defaults to 0)
34068      * @type Number
34069      */
34070     this.minSize = 0;
34071     
34072     /**
34073      * The maximum size of the resizing element. (Defaults to 2000)
34074      * @type Number
34075      */
34076     this.maxSize = 2000;
34077     
34078     /**
34079      * Whether to animate the transition to the new size
34080      * @type Boolean
34081      */
34082     this.animate = false;
34083     
34084     /**
34085      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34086      * @type Boolean
34087      */
34088     this.useShim = false;
34089     
34090     /** @private */
34091     this.shim = null;
34092     
34093     if(!cfg.existingProxy){
34094         /** @private */
34095         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34096     }else{
34097         this.proxy = Roo.get(cfg.existingProxy).dom;
34098     }
34099     /** @private */
34100     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34101     
34102     /** @private */
34103     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34104     
34105     /** @private */
34106     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34107     
34108     /** @private */
34109     this.dragSpecs = {};
34110     
34111     /**
34112      * @private The adapter to use to positon and resize elements
34113      */
34114     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34115     this.adapter.init(this);
34116     
34117     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34118         /** @private */
34119         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34120         this.el.addClass("roo-splitbar-h");
34121     }else{
34122         /** @private */
34123         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34124         this.el.addClass("roo-splitbar-v");
34125     }
34126     
34127     this.addEvents({
34128         /**
34129          * @event resize
34130          * Fires when the splitter is moved (alias for {@link #event-moved})
34131          * @param {Roo.bootstrap.SplitBar} this
34132          * @param {Number} newSize the new width or height
34133          */
34134         "resize" : true,
34135         /**
34136          * @event moved
34137          * Fires when the splitter is moved
34138          * @param {Roo.bootstrap.SplitBar} this
34139          * @param {Number} newSize the new width or height
34140          */
34141         "moved" : true,
34142         /**
34143          * @event beforeresize
34144          * Fires before the splitter is dragged
34145          * @param {Roo.bootstrap.SplitBar} this
34146          */
34147         "beforeresize" : true,
34148
34149         "beforeapply" : true
34150     });
34151
34152     Roo.util.Observable.call(this);
34153 };
34154
34155 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34156     onStartProxyDrag : function(x, y){
34157         this.fireEvent("beforeresize", this);
34158         if(!this.overlay){
34159             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34160             o.unselectable();
34161             o.enableDisplayMode("block");
34162             // all splitbars share the same overlay
34163             Roo.bootstrap.SplitBar.prototype.overlay = o;
34164         }
34165         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34166         this.overlay.show();
34167         Roo.get(this.proxy).setDisplayed("block");
34168         var size = this.adapter.getElementSize(this);
34169         this.activeMinSize = this.getMinimumSize();;
34170         this.activeMaxSize = this.getMaximumSize();;
34171         var c1 = size - this.activeMinSize;
34172         var c2 = Math.max(this.activeMaxSize - size, 0);
34173         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34174             this.dd.resetConstraints();
34175             this.dd.setXConstraint(
34176                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34177                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34178             );
34179             this.dd.setYConstraint(0, 0);
34180         }else{
34181             this.dd.resetConstraints();
34182             this.dd.setXConstraint(0, 0);
34183             this.dd.setYConstraint(
34184                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34185                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34186             );
34187          }
34188         this.dragSpecs.startSize = size;
34189         this.dragSpecs.startPoint = [x, y];
34190         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34191     },
34192     
34193     /** 
34194      * @private Called after the drag operation by the DDProxy
34195      */
34196     onEndProxyDrag : function(e){
34197         Roo.get(this.proxy).setDisplayed(false);
34198         var endPoint = Roo.lib.Event.getXY(e);
34199         if(this.overlay){
34200             this.overlay.hide();
34201         }
34202         var newSize;
34203         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34204             newSize = this.dragSpecs.startSize + 
34205                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34206                     endPoint[0] - this.dragSpecs.startPoint[0] :
34207                     this.dragSpecs.startPoint[0] - endPoint[0]
34208                 );
34209         }else{
34210             newSize = this.dragSpecs.startSize + 
34211                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34212                     endPoint[1] - this.dragSpecs.startPoint[1] :
34213                     this.dragSpecs.startPoint[1] - endPoint[1]
34214                 );
34215         }
34216         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34217         if(newSize != this.dragSpecs.startSize){
34218             if(this.fireEvent('beforeapply', this, newSize) !== false){
34219                 this.adapter.setElementSize(this, newSize);
34220                 this.fireEvent("moved", this, newSize);
34221                 this.fireEvent("resize", this, newSize);
34222             }
34223         }
34224     },
34225     
34226     /**
34227      * Get the adapter this SplitBar uses
34228      * @return The adapter object
34229      */
34230     getAdapter : function(){
34231         return this.adapter;
34232     },
34233     
34234     /**
34235      * Set the adapter this SplitBar uses
34236      * @param {Object} adapter A SplitBar adapter object
34237      */
34238     setAdapter : function(adapter){
34239         this.adapter = adapter;
34240         this.adapter.init(this);
34241     },
34242     
34243     /**
34244      * Gets the minimum size for the resizing element
34245      * @return {Number} The minimum size
34246      */
34247     getMinimumSize : function(){
34248         return this.minSize;
34249     },
34250     
34251     /**
34252      * Sets the minimum size for the resizing element
34253      * @param {Number} minSize The minimum size
34254      */
34255     setMinimumSize : function(minSize){
34256         this.minSize = minSize;
34257     },
34258     
34259     /**
34260      * Gets the maximum size for the resizing element
34261      * @return {Number} The maximum size
34262      */
34263     getMaximumSize : function(){
34264         return this.maxSize;
34265     },
34266     
34267     /**
34268      * Sets the maximum size for the resizing element
34269      * @param {Number} maxSize The maximum size
34270      */
34271     setMaximumSize : function(maxSize){
34272         this.maxSize = maxSize;
34273     },
34274     
34275     /**
34276      * Sets the initialize size for the resizing element
34277      * @param {Number} size The initial size
34278      */
34279     setCurrentSize : function(size){
34280         var oldAnimate = this.animate;
34281         this.animate = false;
34282         this.adapter.setElementSize(this, size);
34283         this.animate = oldAnimate;
34284     },
34285     
34286     /**
34287      * Destroy this splitbar. 
34288      * @param {Boolean} removeEl True to remove the element
34289      */
34290     destroy : function(removeEl){
34291         if(this.shim){
34292             this.shim.remove();
34293         }
34294         this.dd.unreg();
34295         this.proxy.parentNode.removeChild(this.proxy);
34296         if(removeEl){
34297             this.el.remove();
34298         }
34299     }
34300 });
34301
34302 /**
34303  * @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.
34304  */
34305 Roo.bootstrap.SplitBar.createProxy = function(dir){
34306     var proxy = new Roo.Element(document.createElement("div"));
34307     proxy.unselectable();
34308     var cls = 'roo-splitbar-proxy';
34309     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34310     document.body.appendChild(proxy.dom);
34311     return proxy.dom;
34312 };
34313
34314 /** 
34315  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34316  * Default Adapter. It assumes the splitter and resizing element are not positioned
34317  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34318  */
34319 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34320 };
34321
34322 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34323     // do nothing for now
34324     init : function(s){
34325     
34326     },
34327     /**
34328      * Called before drag operations to get the current size of the resizing element. 
34329      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34330      */
34331      getElementSize : function(s){
34332         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34333             return s.resizingEl.getWidth();
34334         }else{
34335             return s.resizingEl.getHeight();
34336         }
34337     },
34338     
34339     /**
34340      * Called after drag operations to set the size of the resizing element.
34341      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34342      * @param {Number} newSize The new size to set
34343      * @param {Function} onComplete A function to be invoked when resizing is complete
34344      */
34345     setElementSize : function(s, newSize, onComplete){
34346         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34347             if(!s.animate){
34348                 s.resizingEl.setWidth(newSize);
34349                 if(onComplete){
34350                     onComplete(s, newSize);
34351                 }
34352             }else{
34353                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34354             }
34355         }else{
34356             
34357             if(!s.animate){
34358                 s.resizingEl.setHeight(newSize);
34359                 if(onComplete){
34360                     onComplete(s, newSize);
34361                 }
34362             }else{
34363                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34364             }
34365         }
34366     }
34367 };
34368
34369 /** 
34370  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34371  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34372  * Adapter that  moves the splitter element to align with the resized sizing element. 
34373  * Used with an absolute positioned SplitBar.
34374  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34375  * document.body, make sure you assign an id to the body element.
34376  */
34377 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34378     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34379     this.container = Roo.get(container);
34380 };
34381
34382 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34383     init : function(s){
34384         this.basic.init(s);
34385     },
34386     
34387     getElementSize : function(s){
34388         return this.basic.getElementSize(s);
34389     },
34390     
34391     setElementSize : function(s, newSize, onComplete){
34392         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34393     },
34394     
34395     moveSplitter : function(s){
34396         var yes = Roo.bootstrap.SplitBar;
34397         switch(s.placement){
34398             case yes.LEFT:
34399                 s.el.setX(s.resizingEl.getRight());
34400                 break;
34401             case yes.RIGHT:
34402                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34403                 break;
34404             case yes.TOP:
34405                 s.el.setY(s.resizingEl.getBottom());
34406                 break;
34407             case yes.BOTTOM:
34408                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34409                 break;
34410         }
34411     }
34412 };
34413
34414 /**
34415  * Orientation constant - Create a vertical SplitBar
34416  * @static
34417  * @type Number
34418  */
34419 Roo.bootstrap.SplitBar.VERTICAL = 1;
34420
34421 /**
34422  * Orientation constant - Create a horizontal SplitBar
34423  * @static
34424  * @type Number
34425  */
34426 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34427
34428 /**
34429  * Placement constant - The resizing element is to the left of the splitter element
34430  * @static
34431  * @type Number
34432  */
34433 Roo.bootstrap.SplitBar.LEFT = 1;
34434
34435 /**
34436  * Placement constant - The resizing element is to the right of the splitter element
34437  * @static
34438  * @type Number
34439  */
34440 Roo.bootstrap.SplitBar.RIGHT = 2;
34441
34442 /**
34443  * Placement constant - The resizing element is positioned above the splitter element
34444  * @static
34445  * @type Number
34446  */
34447 Roo.bootstrap.SplitBar.TOP = 3;
34448
34449 /**
34450  * Placement constant - The resizing element is positioned under splitter element
34451  * @static
34452  * @type Number
34453  */
34454 Roo.bootstrap.SplitBar.BOTTOM = 4;
34455 Roo.namespace("Roo.bootstrap.layout");/*
34456  * Based on:
34457  * Ext JS Library 1.1.1
34458  * Copyright(c) 2006-2007, Ext JS, LLC.
34459  *
34460  * Originally Released Under LGPL - original licence link has changed is not relivant.
34461  *
34462  * Fork - LGPL
34463  * <script type="text/javascript">
34464  */
34465
34466 /**
34467  * @class Roo.bootstrap.layout.Manager
34468  * @extends Roo.bootstrap.Component
34469  * Base class for layout managers.
34470  */
34471 Roo.bootstrap.layout.Manager = function(config)
34472 {
34473     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34474
34475
34476
34477
34478
34479     /** false to disable window resize monitoring @type Boolean */
34480     this.monitorWindowResize = true;
34481     this.regions = {};
34482     this.addEvents({
34483         /**
34484          * @event layout
34485          * Fires when a layout is performed.
34486          * @param {Roo.LayoutManager} this
34487          */
34488         "layout" : true,
34489         /**
34490          * @event regionresized
34491          * Fires when the user resizes a region.
34492          * @param {Roo.LayoutRegion} region The resized region
34493          * @param {Number} newSize The new size (width for east/west, height for north/south)
34494          */
34495         "regionresized" : true,
34496         /**
34497          * @event regioncollapsed
34498          * Fires when a region is collapsed.
34499          * @param {Roo.LayoutRegion} region The collapsed region
34500          */
34501         "regioncollapsed" : true,
34502         /**
34503          * @event regionexpanded
34504          * Fires when a region is expanded.
34505          * @param {Roo.LayoutRegion} region The expanded region
34506          */
34507         "regionexpanded" : true
34508     });
34509     this.updating = false;
34510
34511     if (config.el) {
34512         this.el = Roo.get(config.el);
34513         this.initEvents();
34514     }
34515
34516 };
34517
34518 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34519
34520
34521     regions : null,
34522
34523     monitorWindowResize : true,
34524
34525
34526     updating : false,
34527
34528
34529     onRender : function(ct, position)
34530     {
34531         if(!this.el){
34532             this.el = Roo.get(ct);
34533             this.initEvents();
34534         }
34535         //this.fireEvent('render',this);
34536     },
34537
34538
34539     initEvents: function()
34540     {
34541
34542
34543         // ie scrollbar fix
34544         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34545             document.body.scroll = "no";
34546         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34547             this.el.position('relative');
34548         }
34549         this.id = this.el.id;
34550         this.el.addClass("roo-layout-container");
34551         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34552         if(this.el.dom != document.body ) {
34553             this.el.on('resize', this.layout,this);
34554             this.el.on('show', this.layout,this);
34555         }
34556
34557     },
34558
34559     /**
34560      * Returns true if this layout is currently being updated
34561      * @return {Boolean}
34562      */
34563     isUpdating : function(){
34564         return this.updating;
34565     },
34566
34567     /**
34568      * Suspend the LayoutManager from doing auto-layouts while
34569      * making multiple add or remove calls
34570      */
34571     beginUpdate : function(){
34572         this.updating = true;
34573     },
34574
34575     /**
34576      * Restore auto-layouts and optionally disable the manager from performing a layout
34577      * @param {Boolean} noLayout true to disable a layout update
34578      */
34579     endUpdate : function(noLayout){
34580         this.updating = false;
34581         if(!noLayout){
34582             this.layout();
34583         }
34584     },
34585
34586     layout: function(){
34587         // abstract...
34588     },
34589
34590     onRegionResized : function(region, newSize){
34591         this.fireEvent("regionresized", region, newSize);
34592         this.layout();
34593     },
34594
34595     onRegionCollapsed : function(region){
34596         this.fireEvent("regioncollapsed", region);
34597     },
34598
34599     onRegionExpanded : function(region){
34600         this.fireEvent("regionexpanded", region);
34601     },
34602
34603     /**
34604      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34605      * performs box-model adjustments.
34606      * @return {Object} The size as an object {width: (the width), height: (the height)}
34607      */
34608     getViewSize : function()
34609     {
34610         var size;
34611         if(this.el.dom != document.body){
34612             size = this.el.getSize();
34613         }else{
34614             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34615         }
34616         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34617         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34618         return size;
34619     },
34620
34621     /**
34622      * Returns the Element this layout is bound to.
34623      * @return {Roo.Element}
34624      */
34625     getEl : function(){
34626         return this.el;
34627     },
34628
34629     /**
34630      * Returns the specified region.
34631      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34632      * @return {Roo.LayoutRegion}
34633      */
34634     getRegion : function(target){
34635         return this.regions[target.toLowerCase()];
34636     },
34637
34638     onWindowResize : function(){
34639         if(this.monitorWindowResize){
34640             this.layout();
34641         }
34642     }
34643 });
34644 /*
34645  * Based on:
34646  * Ext JS Library 1.1.1
34647  * Copyright(c) 2006-2007, Ext JS, LLC.
34648  *
34649  * Originally Released Under LGPL - original licence link has changed is not relivant.
34650  *
34651  * Fork - LGPL
34652  * <script type="text/javascript">
34653  */
34654 /**
34655  * @class Roo.bootstrap.layout.Border
34656  * @extends Roo.bootstrap.layout.Manager
34657  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34658  * please see: examples/bootstrap/nested.html<br><br>
34659  
34660 <b>The container the layout is rendered into can be either the body element or any other element.
34661 If it is not the body element, the container needs to either be an absolute positioned element,
34662 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34663 the container size if it is not the body element.</b>
34664
34665 * @constructor
34666 * Create a new Border
34667 * @param {Object} config Configuration options
34668  */
34669 Roo.bootstrap.layout.Border = function(config){
34670     config = config || {};
34671     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34672     
34673     
34674     
34675     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34676         if(config[region]){
34677             config[region].region = region;
34678             this.addRegion(config[region]);
34679         }
34680     },this);
34681     
34682 };
34683
34684 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34685
34686 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34687     /**
34688      * Creates and adds a new region if it doesn't already exist.
34689      * @param {String} target The target region key (north, south, east, west or center).
34690      * @param {Object} config The regions config object
34691      * @return {BorderLayoutRegion} The new region
34692      */
34693     addRegion : function(config)
34694     {
34695         if(!this.regions[config.region]){
34696             var r = this.factory(config);
34697             this.bindRegion(r);
34698         }
34699         return this.regions[config.region];
34700     },
34701
34702     // private (kinda)
34703     bindRegion : function(r){
34704         this.regions[r.config.region] = r;
34705         
34706         r.on("visibilitychange",    this.layout, this);
34707         r.on("paneladded",          this.layout, this);
34708         r.on("panelremoved",        this.layout, this);
34709         r.on("invalidated",         this.layout, this);
34710         r.on("resized",             this.onRegionResized, this);
34711         r.on("collapsed",           this.onRegionCollapsed, this);
34712         r.on("expanded",            this.onRegionExpanded, this);
34713     },
34714
34715     /**
34716      * Performs a layout update.
34717      */
34718     layout : function()
34719     {
34720         if(this.updating) {
34721             return;
34722         }
34723         
34724         // render all the rebions if they have not been done alreayd?
34725         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34726             if(this.regions[region] && !this.regions[region].bodyEl){
34727                 this.regions[region].onRender(this.el)
34728             }
34729         },this);
34730         
34731         var size = this.getViewSize();
34732         var w = size.width;
34733         var h = size.height;
34734         var centerW = w;
34735         var centerH = h;
34736         var centerY = 0;
34737         var centerX = 0;
34738         //var x = 0, y = 0;
34739
34740         var rs = this.regions;
34741         var north = rs["north"];
34742         var south = rs["south"]; 
34743         var west = rs["west"];
34744         var east = rs["east"];
34745         var center = rs["center"];
34746         //if(this.hideOnLayout){ // not supported anymore
34747             //c.el.setStyle("display", "none");
34748         //}
34749         if(north && north.isVisible()){
34750             var b = north.getBox();
34751             var m = north.getMargins();
34752             b.width = w - (m.left+m.right);
34753             b.x = m.left;
34754             b.y = m.top;
34755             centerY = b.height + b.y + m.bottom;
34756             centerH -= centerY;
34757             north.updateBox(this.safeBox(b));
34758         }
34759         if(south && south.isVisible()){
34760             var b = south.getBox();
34761             var m = south.getMargins();
34762             b.width = w - (m.left+m.right);
34763             b.x = m.left;
34764             var totalHeight = (b.height + m.top + m.bottom);
34765             b.y = h - totalHeight + m.top;
34766             centerH -= totalHeight;
34767             south.updateBox(this.safeBox(b));
34768         }
34769         if(west && west.isVisible()){
34770             var b = west.getBox();
34771             var m = west.getMargins();
34772             b.height = centerH - (m.top+m.bottom);
34773             b.x = m.left;
34774             b.y = centerY + m.top;
34775             var totalWidth = (b.width + m.left + m.right);
34776             centerX += totalWidth;
34777             centerW -= totalWidth;
34778             west.updateBox(this.safeBox(b));
34779         }
34780         if(east && east.isVisible()){
34781             var b = east.getBox();
34782             var m = east.getMargins();
34783             b.height = centerH - (m.top+m.bottom);
34784             var totalWidth = (b.width + m.left + m.right);
34785             b.x = w - totalWidth + m.left;
34786             b.y = centerY + m.top;
34787             centerW -= totalWidth;
34788             east.updateBox(this.safeBox(b));
34789         }
34790         if(center){
34791             var m = center.getMargins();
34792             var centerBox = {
34793                 x: centerX + m.left,
34794                 y: centerY + m.top,
34795                 width: centerW - (m.left+m.right),
34796                 height: centerH - (m.top+m.bottom)
34797             };
34798             //if(this.hideOnLayout){
34799                 //center.el.setStyle("display", "block");
34800             //}
34801             center.updateBox(this.safeBox(centerBox));
34802         }
34803         this.el.repaint();
34804         this.fireEvent("layout", this);
34805     },
34806
34807     // private
34808     safeBox : function(box){
34809         box.width = Math.max(0, box.width);
34810         box.height = Math.max(0, box.height);
34811         return box;
34812     },
34813
34814     /**
34815      * Adds a ContentPanel (or subclass) to this layout.
34816      * @param {String} target The target region key (north, south, east, west or center).
34817      * @param {Roo.ContentPanel} panel The panel to add
34818      * @return {Roo.ContentPanel} The added panel
34819      */
34820     add : function(target, panel){
34821          
34822         target = target.toLowerCase();
34823         return this.regions[target].add(panel);
34824     },
34825
34826     /**
34827      * Remove a ContentPanel (or subclass) to this layout.
34828      * @param {String} target The target region key (north, south, east, west or center).
34829      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34830      * @return {Roo.ContentPanel} The removed panel
34831      */
34832     remove : function(target, panel){
34833         target = target.toLowerCase();
34834         return this.regions[target].remove(panel);
34835     },
34836
34837     /**
34838      * Searches all regions for a panel with the specified id
34839      * @param {String} panelId
34840      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34841      */
34842     findPanel : function(panelId){
34843         var rs = this.regions;
34844         for(var target in rs){
34845             if(typeof rs[target] != "function"){
34846                 var p = rs[target].getPanel(panelId);
34847                 if(p){
34848                     return p;
34849                 }
34850             }
34851         }
34852         return null;
34853     },
34854
34855     /**
34856      * Searches all regions for a panel with the specified id and activates (shows) it.
34857      * @param {String/ContentPanel} panelId The panels id or the panel itself
34858      * @return {Roo.ContentPanel} The shown panel or null
34859      */
34860     showPanel : function(panelId) {
34861       var rs = this.regions;
34862       for(var target in rs){
34863          var r = rs[target];
34864          if(typeof r != "function"){
34865             if(r.hasPanel(panelId)){
34866                return r.showPanel(panelId);
34867             }
34868          }
34869       }
34870       return null;
34871    },
34872
34873    /**
34874      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34875      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34876      */
34877    /*
34878     restoreState : function(provider){
34879         if(!provider){
34880             provider = Roo.state.Manager;
34881         }
34882         var sm = new Roo.LayoutStateManager();
34883         sm.init(this, provider);
34884     },
34885 */
34886  
34887  
34888     /**
34889      * Adds a xtype elements to the layout.
34890      * <pre><code>
34891
34892 layout.addxtype({
34893        xtype : 'ContentPanel',
34894        region: 'west',
34895        items: [ .... ]
34896    }
34897 );
34898
34899 layout.addxtype({
34900         xtype : 'NestedLayoutPanel',
34901         region: 'west',
34902         layout: {
34903            center: { },
34904            west: { }   
34905         },
34906         items : [ ... list of content panels or nested layout panels.. ]
34907    }
34908 );
34909 </code></pre>
34910      * @param {Object} cfg Xtype definition of item to add.
34911      */
34912     addxtype : function(cfg)
34913     {
34914         // basically accepts a pannel...
34915         // can accept a layout region..!?!?
34916         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34917         
34918         
34919         // theory?  children can only be panels??
34920         
34921         //if (!cfg.xtype.match(/Panel$/)) {
34922         //    return false;
34923         //}
34924         var ret = false;
34925         
34926         if (typeof(cfg.region) == 'undefined') {
34927             Roo.log("Failed to add Panel, region was not set");
34928             Roo.log(cfg);
34929             return false;
34930         }
34931         var region = cfg.region;
34932         delete cfg.region;
34933         
34934           
34935         var xitems = [];
34936         if (cfg.items) {
34937             xitems = cfg.items;
34938             delete cfg.items;
34939         }
34940         var nb = false;
34941         
34942         switch(cfg.xtype) 
34943         {
34944             case 'Content':  // ContentPanel (el, cfg)
34945             case 'Scroll':  // ContentPanel (el, cfg)
34946             case 'View': 
34947                 cfg.autoCreate = true;
34948                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34949                 //} else {
34950                 //    var el = this.el.createChild();
34951                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34952                 //}
34953                 
34954                 this.add(region, ret);
34955                 break;
34956             
34957             /*
34958             case 'TreePanel': // our new panel!
34959                 cfg.el = this.el.createChild();
34960                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34961                 this.add(region, ret);
34962                 break;
34963             */
34964             
34965             case 'Nest': 
34966                 // create a new Layout (which is  a Border Layout...
34967                 
34968                 var clayout = cfg.layout;
34969                 clayout.el  = this.el.createChild();
34970                 clayout.items   = clayout.items  || [];
34971                 
34972                 delete cfg.layout;
34973                 
34974                 // replace this exitems with the clayout ones..
34975                 xitems = clayout.items;
34976                  
34977                 // force background off if it's in center...
34978                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34979                     cfg.background = false;
34980                 }
34981                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34982                 
34983                 
34984                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34985                 //console.log('adding nested layout panel '  + cfg.toSource());
34986                 this.add(region, ret);
34987                 nb = {}; /// find first...
34988                 break;
34989             
34990             case 'Grid':
34991                 
34992                 // needs grid and region
34993                 
34994                 //var el = this.getRegion(region).el.createChild();
34995                 /*
34996                  *var el = this.el.createChild();
34997                 // create the grid first...
34998                 cfg.grid.container = el;
34999                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35000                 */
35001                 
35002                 if (region == 'center' && this.active ) {
35003                     cfg.background = false;
35004                 }
35005                 
35006                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35007                 
35008                 this.add(region, ret);
35009                 /*
35010                 if (cfg.background) {
35011                     // render grid on panel activation (if panel background)
35012                     ret.on('activate', function(gp) {
35013                         if (!gp.grid.rendered) {
35014                     //        gp.grid.render(el);
35015                         }
35016                     });
35017                 } else {
35018                   //  cfg.grid.render(el);
35019                 }
35020                 */
35021                 break;
35022            
35023            
35024             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35025                 // it was the old xcomponent building that caused this before.
35026                 // espeically if border is the top element in the tree.
35027                 ret = this;
35028                 break; 
35029                 
35030                     
35031                 
35032                 
35033                 
35034             default:
35035                 /*
35036                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35037                     
35038                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35039                     this.add(region, ret);
35040                 } else {
35041                 */
35042                     Roo.log(cfg);
35043                     throw "Can not add '" + cfg.xtype + "' to Border";
35044                     return null;
35045              
35046                                 
35047              
35048         }
35049         this.beginUpdate();
35050         // add children..
35051         var region = '';
35052         var abn = {};
35053         Roo.each(xitems, function(i)  {
35054             region = nb && i.region ? i.region : false;
35055             
35056             var add = ret.addxtype(i);
35057            
35058             if (region) {
35059                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35060                 if (!i.background) {
35061                     abn[region] = nb[region] ;
35062                 }
35063             }
35064             
35065         });
35066         this.endUpdate();
35067
35068         // make the last non-background panel active..
35069         //if (nb) { Roo.log(abn); }
35070         if (nb) {
35071             
35072             for(var r in abn) {
35073                 region = this.getRegion(r);
35074                 if (region) {
35075                     // tried using nb[r], but it does not work..
35076                      
35077                     region.showPanel(abn[r]);
35078                    
35079                 }
35080             }
35081         }
35082         return ret;
35083         
35084     },
35085     
35086     
35087 // private
35088     factory : function(cfg)
35089     {
35090         
35091         var validRegions = Roo.bootstrap.layout.Border.regions;
35092
35093         var target = cfg.region;
35094         cfg.mgr = this;
35095         
35096         var r = Roo.bootstrap.layout;
35097         Roo.log(target);
35098         switch(target){
35099             case "north":
35100                 return new r.North(cfg);
35101             case "south":
35102                 return new r.South(cfg);
35103             case "east":
35104                 return new r.East(cfg);
35105             case "west":
35106                 return new r.West(cfg);
35107             case "center":
35108                 return new r.Center(cfg);
35109         }
35110         throw 'Layout region "'+target+'" not supported.';
35111     }
35112     
35113     
35114 });
35115  /*
35116  * Based on:
35117  * Ext JS Library 1.1.1
35118  * Copyright(c) 2006-2007, Ext JS, LLC.
35119  *
35120  * Originally Released Under LGPL - original licence link has changed is not relivant.
35121  *
35122  * Fork - LGPL
35123  * <script type="text/javascript">
35124  */
35125  
35126 /**
35127  * @class Roo.bootstrap.layout.Basic
35128  * @extends Roo.util.Observable
35129  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35130  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35131  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35132  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35133  * @cfg {string}   region  the region that it inhabits..
35134  * @cfg {bool}   skipConfig skip config?
35135  * 
35136
35137  */
35138 Roo.bootstrap.layout.Basic = function(config){
35139     
35140     this.mgr = config.mgr;
35141     
35142     this.position = config.region;
35143     
35144     var skipConfig = config.skipConfig;
35145     
35146     this.events = {
35147         /**
35148          * @scope Roo.BasicLayoutRegion
35149          */
35150         
35151         /**
35152          * @event beforeremove
35153          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35154          * @param {Roo.LayoutRegion} this
35155          * @param {Roo.ContentPanel} panel The panel
35156          * @param {Object} e The cancel event object
35157          */
35158         "beforeremove" : true,
35159         /**
35160          * @event invalidated
35161          * Fires when the layout for this region is changed.
35162          * @param {Roo.LayoutRegion} this
35163          */
35164         "invalidated" : true,
35165         /**
35166          * @event visibilitychange
35167          * Fires when this region is shown or hidden 
35168          * @param {Roo.LayoutRegion} this
35169          * @param {Boolean} visibility true or false
35170          */
35171         "visibilitychange" : true,
35172         /**
35173          * @event paneladded
35174          * Fires when a panel is added. 
35175          * @param {Roo.LayoutRegion} this
35176          * @param {Roo.ContentPanel} panel The panel
35177          */
35178         "paneladded" : true,
35179         /**
35180          * @event panelremoved
35181          * Fires when a panel is removed. 
35182          * @param {Roo.LayoutRegion} this
35183          * @param {Roo.ContentPanel} panel The panel
35184          */
35185         "panelremoved" : true,
35186         /**
35187          * @event beforecollapse
35188          * Fires when this region before collapse.
35189          * @param {Roo.LayoutRegion} this
35190          */
35191         "beforecollapse" : true,
35192         /**
35193          * @event collapsed
35194          * Fires when this region is collapsed.
35195          * @param {Roo.LayoutRegion} this
35196          */
35197         "collapsed" : true,
35198         /**
35199          * @event expanded
35200          * Fires when this region is expanded.
35201          * @param {Roo.LayoutRegion} this
35202          */
35203         "expanded" : true,
35204         /**
35205          * @event slideshow
35206          * Fires when this region is slid into view.
35207          * @param {Roo.LayoutRegion} this
35208          */
35209         "slideshow" : true,
35210         /**
35211          * @event slidehide
35212          * Fires when this region slides out of view. 
35213          * @param {Roo.LayoutRegion} this
35214          */
35215         "slidehide" : true,
35216         /**
35217          * @event panelactivated
35218          * Fires when a panel is activated. 
35219          * @param {Roo.LayoutRegion} this
35220          * @param {Roo.ContentPanel} panel The activated panel
35221          */
35222         "panelactivated" : true,
35223         /**
35224          * @event resized
35225          * Fires when the user resizes this region. 
35226          * @param {Roo.LayoutRegion} this
35227          * @param {Number} newSize The new size (width for east/west, height for north/south)
35228          */
35229         "resized" : true
35230     };
35231     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35232     this.panels = new Roo.util.MixedCollection();
35233     this.panels.getKey = this.getPanelId.createDelegate(this);
35234     this.box = null;
35235     this.activePanel = null;
35236     // ensure listeners are added...
35237     
35238     if (config.listeners || config.events) {
35239         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35240             listeners : config.listeners || {},
35241             events : config.events || {}
35242         });
35243     }
35244     
35245     if(skipConfig !== true){
35246         this.applyConfig(config);
35247     }
35248 };
35249
35250 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35251 {
35252     getPanelId : function(p){
35253         return p.getId();
35254     },
35255     
35256     applyConfig : function(config){
35257         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35258         this.config = config;
35259         
35260     },
35261     
35262     /**
35263      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35264      * the width, for horizontal (north, south) the height.
35265      * @param {Number} newSize The new width or height
35266      */
35267     resizeTo : function(newSize){
35268         var el = this.el ? this.el :
35269                  (this.activePanel ? this.activePanel.getEl() : null);
35270         if(el){
35271             switch(this.position){
35272                 case "east":
35273                 case "west":
35274                     el.setWidth(newSize);
35275                     this.fireEvent("resized", this, newSize);
35276                 break;
35277                 case "north":
35278                 case "south":
35279                     el.setHeight(newSize);
35280                     this.fireEvent("resized", this, newSize);
35281                 break;                
35282             }
35283         }
35284     },
35285     
35286     getBox : function(){
35287         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35288     },
35289     
35290     getMargins : function(){
35291         return this.margins;
35292     },
35293     
35294     updateBox : function(box){
35295         this.box = box;
35296         var el = this.activePanel.getEl();
35297         el.dom.style.left = box.x + "px";
35298         el.dom.style.top = box.y + "px";
35299         this.activePanel.setSize(box.width, box.height);
35300     },
35301     
35302     /**
35303      * Returns the container element for this region.
35304      * @return {Roo.Element}
35305      */
35306     getEl : function(){
35307         return this.activePanel;
35308     },
35309     
35310     /**
35311      * Returns true if this region is currently visible.
35312      * @return {Boolean}
35313      */
35314     isVisible : function(){
35315         return this.activePanel ? true : false;
35316     },
35317     
35318     setActivePanel : function(panel){
35319         panel = this.getPanel(panel);
35320         if(this.activePanel && this.activePanel != panel){
35321             this.activePanel.setActiveState(false);
35322             this.activePanel.getEl().setLeftTop(-10000,-10000);
35323         }
35324         this.activePanel = panel;
35325         panel.setActiveState(true);
35326         if(this.box){
35327             panel.setSize(this.box.width, this.box.height);
35328         }
35329         this.fireEvent("panelactivated", this, panel);
35330         this.fireEvent("invalidated");
35331     },
35332     
35333     /**
35334      * Show the specified panel.
35335      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35336      * @return {Roo.ContentPanel} The shown panel or null
35337      */
35338     showPanel : function(panel){
35339         panel = this.getPanel(panel);
35340         if(panel){
35341             this.setActivePanel(panel);
35342         }
35343         return panel;
35344     },
35345     
35346     /**
35347      * Get the active panel for this region.
35348      * @return {Roo.ContentPanel} The active panel or null
35349      */
35350     getActivePanel : function(){
35351         return this.activePanel;
35352     },
35353     
35354     /**
35355      * Add the passed ContentPanel(s)
35356      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35357      * @return {Roo.ContentPanel} The panel added (if only one was added)
35358      */
35359     add : function(panel){
35360         if(arguments.length > 1){
35361             for(var i = 0, len = arguments.length; i < len; i++) {
35362                 this.add(arguments[i]);
35363             }
35364             return null;
35365         }
35366         if(this.hasPanel(panel)){
35367             this.showPanel(panel);
35368             return panel;
35369         }
35370         var el = panel.getEl();
35371         if(el.dom.parentNode != this.mgr.el.dom){
35372             this.mgr.el.dom.appendChild(el.dom);
35373         }
35374         if(panel.setRegion){
35375             panel.setRegion(this);
35376         }
35377         this.panels.add(panel);
35378         el.setStyle("position", "absolute");
35379         if(!panel.background){
35380             this.setActivePanel(panel);
35381             if(this.config.initialSize && this.panels.getCount()==1){
35382                 this.resizeTo(this.config.initialSize);
35383             }
35384         }
35385         this.fireEvent("paneladded", this, panel);
35386         return panel;
35387     },
35388     
35389     /**
35390      * Returns true if the panel is in this region.
35391      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35392      * @return {Boolean}
35393      */
35394     hasPanel : function(panel){
35395         if(typeof panel == "object"){ // must be panel obj
35396             panel = panel.getId();
35397         }
35398         return this.getPanel(panel) ? true : false;
35399     },
35400     
35401     /**
35402      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35403      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35404      * @param {Boolean} preservePanel Overrides the config preservePanel option
35405      * @return {Roo.ContentPanel} The panel that was removed
35406      */
35407     remove : function(panel, preservePanel){
35408         panel = this.getPanel(panel);
35409         if(!panel){
35410             return null;
35411         }
35412         var e = {};
35413         this.fireEvent("beforeremove", this, panel, e);
35414         if(e.cancel === true){
35415             return null;
35416         }
35417         var panelId = panel.getId();
35418         this.panels.removeKey(panelId);
35419         return panel;
35420     },
35421     
35422     /**
35423      * Returns the panel specified or null if it's not in this region.
35424      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35425      * @return {Roo.ContentPanel}
35426      */
35427     getPanel : function(id){
35428         if(typeof id == "object"){ // must be panel obj
35429             return id;
35430         }
35431         return this.panels.get(id);
35432     },
35433     
35434     /**
35435      * Returns this regions position (north/south/east/west/center).
35436      * @return {String} 
35437      */
35438     getPosition: function(){
35439         return this.position;    
35440     }
35441 });/*
35442  * Based on:
35443  * Ext JS Library 1.1.1
35444  * Copyright(c) 2006-2007, Ext JS, LLC.
35445  *
35446  * Originally Released Under LGPL - original licence link has changed is not relivant.
35447  *
35448  * Fork - LGPL
35449  * <script type="text/javascript">
35450  */
35451  
35452 /**
35453  * @class Roo.bootstrap.layout.Region
35454  * @extends Roo.bootstrap.layout.Basic
35455  * This class represents a region in a layout manager.
35456  
35457  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35458  * @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})
35459  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35460  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35461  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35462  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35463  * @cfg {String}    title           The title for the region (overrides panel titles)
35464  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35465  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35466  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35467  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35468  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35469  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35470  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35471  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35472  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35473  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35474
35475  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35476  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35477  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35478  * @cfg {Number}    width           For East/West panels
35479  * @cfg {Number}    height          For North/South panels
35480  * @cfg {Boolean}   split           To show the splitter
35481  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35482  * 
35483  * @cfg {string}   cls             Extra CSS classes to add to region
35484  * 
35485  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35486  * @cfg {string}   region  the region that it inhabits..
35487  *
35488
35489  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35490  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35491
35492  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35493  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35494  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35495  */
35496 Roo.bootstrap.layout.Region = function(config)
35497 {
35498     this.applyConfig(config);
35499
35500     var mgr = config.mgr;
35501     var pos = config.region;
35502     config.skipConfig = true;
35503     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35504     
35505     if (mgr.el) {
35506         this.onRender(mgr.el);   
35507     }
35508      
35509     this.visible = true;
35510     this.collapsed = false;
35511     this.unrendered_panels = [];
35512 };
35513
35514 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35515
35516     position: '', // set by wrapper (eg. north/south etc..)
35517     unrendered_panels : null,  // unrendered panels.
35518     createBody : function(){
35519         /** This region's body element 
35520         * @type Roo.Element */
35521         this.bodyEl = this.el.createChild({
35522                 tag: "div",
35523                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35524         });
35525     },
35526
35527     onRender: function(ctr, pos)
35528     {
35529         var dh = Roo.DomHelper;
35530         /** This region's container element 
35531         * @type Roo.Element */
35532         this.el = dh.append(ctr.dom, {
35533                 tag: "div",
35534                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35535             }, true);
35536         /** This region's title element 
35537         * @type Roo.Element */
35538     
35539         this.titleEl = dh.append(this.el.dom,
35540             {
35541                     tag: "div",
35542                     unselectable: "on",
35543                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35544                     children:[
35545                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35546                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35547                     ]}, true);
35548         
35549         this.titleEl.enableDisplayMode();
35550         /** This region's title text element 
35551         * @type HTMLElement */
35552         this.titleTextEl = this.titleEl.dom.firstChild;
35553         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35554         /*
35555         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35556         this.closeBtn.enableDisplayMode();
35557         this.closeBtn.on("click", this.closeClicked, this);
35558         this.closeBtn.hide();
35559     */
35560         this.createBody(this.config);
35561         if(this.config.hideWhenEmpty){
35562             this.hide();
35563             this.on("paneladded", this.validateVisibility, this);
35564             this.on("panelremoved", this.validateVisibility, this);
35565         }
35566         if(this.autoScroll){
35567             this.bodyEl.setStyle("overflow", "auto");
35568         }else{
35569             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35570         }
35571         //if(c.titlebar !== false){
35572             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35573                 this.titleEl.hide();
35574             }else{
35575                 this.titleEl.show();
35576                 if(this.config.title){
35577                     this.titleTextEl.innerHTML = this.config.title;
35578                 }
35579             }
35580         //}
35581         if(this.config.collapsed){
35582             this.collapse(true);
35583         }
35584         if(this.config.hidden){
35585             this.hide();
35586         }
35587         
35588         if (this.unrendered_panels && this.unrendered_panels.length) {
35589             for (var i =0;i< this.unrendered_panels.length; i++) {
35590                 this.add(this.unrendered_panels[i]);
35591             }
35592             this.unrendered_panels = null;
35593             
35594         }
35595         
35596     },
35597     
35598     applyConfig : function(c)
35599     {
35600         /*
35601          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35602             var dh = Roo.DomHelper;
35603             if(c.titlebar !== false){
35604                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35605                 this.collapseBtn.on("click", this.collapse, this);
35606                 this.collapseBtn.enableDisplayMode();
35607                 /*
35608                 if(c.showPin === true || this.showPin){
35609                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35610                     this.stickBtn.enableDisplayMode();
35611                     this.stickBtn.on("click", this.expand, this);
35612                     this.stickBtn.hide();
35613                 }
35614                 
35615             }
35616             */
35617             /** This region's collapsed element
35618             * @type Roo.Element */
35619             /*
35620              *
35621             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35622                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35623             ]}, true);
35624             
35625             if(c.floatable !== false){
35626                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35627                this.collapsedEl.on("click", this.collapseClick, this);
35628             }
35629
35630             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35631                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35632                    id: "message", unselectable: "on", style:{"float":"left"}});
35633                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35634              }
35635             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35636             this.expandBtn.on("click", this.expand, this);
35637             
35638         }
35639         
35640         if(this.collapseBtn){
35641             this.collapseBtn.setVisible(c.collapsible == true);
35642         }
35643         
35644         this.cmargins = c.cmargins || this.cmargins ||
35645                          (this.position == "west" || this.position == "east" ?
35646                              {top: 0, left: 2, right:2, bottom: 0} :
35647                              {top: 2, left: 0, right:0, bottom: 2});
35648         */
35649         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35650         
35651         
35652         this.bottomTabs = c.tabPosition != "top";
35653         
35654         this.autoScroll = c.autoScroll || false;
35655         
35656         
35657        
35658         
35659         this.duration = c.duration || .30;
35660         this.slideDuration = c.slideDuration || .45;
35661         this.config = c;
35662        
35663     },
35664     /**
35665      * Returns true if this region is currently visible.
35666      * @return {Boolean}
35667      */
35668     isVisible : function(){
35669         return this.visible;
35670     },
35671
35672     /**
35673      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35674      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35675      */
35676     //setCollapsedTitle : function(title){
35677     //    title = title || "&#160;";
35678      //   if(this.collapsedTitleTextEl){
35679       //      this.collapsedTitleTextEl.innerHTML = title;
35680        // }
35681     //},
35682
35683     getBox : function(){
35684         var b;
35685       //  if(!this.collapsed){
35686             b = this.el.getBox(false, true);
35687        // }else{
35688           //  b = this.collapsedEl.getBox(false, true);
35689         //}
35690         return b;
35691     },
35692
35693     getMargins : function(){
35694         return this.margins;
35695         //return this.collapsed ? this.cmargins : this.margins;
35696     },
35697 /*
35698     highlight : function(){
35699         this.el.addClass("x-layout-panel-dragover");
35700     },
35701
35702     unhighlight : function(){
35703         this.el.removeClass("x-layout-panel-dragover");
35704     },
35705 */
35706     updateBox : function(box)
35707     {
35708         if (!this.bodyEl) {
35709             return; // not rendered yet..
35710         }
35711         
35712         this.box = box;
35713         if(!this.collapsed){
35714             this.el.dom.style.left = box.x + "px";
35715             this.el.dom.style.top = box.y + "px";
35716             this.updateBody(box.width, box.height);
35717         }else{
35718             this.collapsedEl.dom.style.left = box.x + "px";
35719             this.collapsedEl.dom.style.top = box.y + "px";
35720             this.collapsedEl.setSize(box.width, box.height);
35721         }
35722         if(this.tabs){
35723             this.tabs.autoSizeTabs();
35724         }
35725     },
35726
35727     updateBody : function(w, h)
35728     {
35729         if(w !== null){
35730             this.el.setWidth(w);
35731             w -= this.el.getBorderWidth("rl");
35732             if(this.config.adjustments){
35733                 w += this.config.adjustments[0];
35734             }
35735         }
35736         if(h !== null && h > 0){
35737             this.el.setHeight(h);
35738             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35739             h -= this.el.getBorderWidth("tb");
35740             if(this.config.adjustments){
35741                 h += this.config.adjustments[1];
35742             }
35743             this.bodyEl.setHeight(h);
35744             if(this.tabs){
35745                 h = this.tabs.syncHeight(h);
35746             }
35747         }
35748         if(this.panelSize){
35749             w = w !== null ? w : this.panelSize.width;
35750             h = h !== null ? h : this.panelSize.height;
35751         }
35752         if(this.activePanel){
35753             var el = this.activePanel.getEl();
35754             w = w !== null ? w : el.getWidth();
35755             h = h !== null ? h : el.getHeight();
35756             this.panelSize = {width: w, height: h};
35757             this.activePanel.setSize(w, h);
35758         }
35759         if(Roo.isIE && this.tabs){
35760             this.tabs.el.repaint();
35761         }
35762     },
35763
35764     /**
35765      * Returns the container element for this region.
35766      * @return {Roo.Element}
35767      */
35768     getEl : function(){
35769         return this.el;
35770     },
35771
35772     /**
35773      * Hides this region.
35774      */
35775     hide : function(){
35776         //if(!this.collapsed){
35777             this.el.dom.style.left = "-2000px";
35778             this.el.hide();
35779         //}else{
35780          //   this.collapsedEl.dom.style.left = "-2000px";
35781          //   this.collapsedEl.hide();
35782        // }
35783         this.visible = false;
35784         this.fireEvent("visibilitychange", this, false);
35785     },
35786
35787     /**
35788      * Shows this region if it was previously hidden.
35789      */
35790     show : function(){
35791         //if(!this.collapsed){
35792             this.el.show();
35793         //}else{
35794         //    this.collapsedEl.show();
35795        // }
35796         this.visible = true;
35797         this.fireEvent("visibilitychange", this, true);
35798     },
35799 /*
35800     closeClicked : function(){
35801         if(this.activePanel){
35802             this.remove(this.activePanel);
35803         }
35804     },
35805
35806     collapseClick : function(e){
35807         if(this.isSlid){
35808            e.stopPropagation();
35809            this.slideIn();
35810         }else{
35811            e.stopPropagation();
35812            this.slideOut();
35813         }
35814     },
35815 */
35816     /**
35817      * Collapses this region.
35818      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35819      */
35820     /*
35821     collapse : function(skipAnim, skipCheck = false){
35822         if(this.collapsed) {
35823             return;
35824         }
35825         
35826         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35827             
35828             this.collapsed = true;
35829             if(this.split){
35830                 this.split.el.hide();
35831             }
35832             if(this.config.animate && skipAnim !== true){
35833                 this.fireEvent("invalidated", this);
35834                 this.animateCollapse();
35835             }else{
35836                 this.el.setLocation(-20000,-20000);
35837                 this.el.hide();
35838                 this.collapsedEl.show();
35839                 this.fireEvent("collapsed", this);
35840                 this.fireEvent("invalidated", this);
35841             }
35842         }
35843         
35844     },
35845 */
35846     animateCollapse : function(){
35847         // overridden
35848     },
35849
35850     /**
35851      * Expands this region if it was previously collapsed.
35852      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35853      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35854      */
35855     /*
35856     expand : function(e, skipAnim){
35857         if(e) {
35858             e.stopPropagation();
35859         }
35860         if(!this.collapsed || this.el.hasActiveFx()) {
35861             return;
35862         }
35863         if(this.isSlid){
35864             this.afterSlideIn();
35865             skipAnim = true;
35866         }
35867         this.collapsed = false;
35868         if(this.config.animate && skipAnim !== true){
35869             this.animateExpand();
35870         }else{
35871             this.el.show();
35872             if(this.split){
35873                 this.split.el.show();
35874             }
35875             this.collapsedEl.setLocation(-2000,-2000);
35876             this.collapsedEl.hide();
35877             this.fireEvent("invalidated", this);
35878             this.fireEvent("expanded", this);
35879         }
35880     },
35881 */
35882     animateExpand : function(){
35883         // overridden
35884     },
35885
35886     initTabs : function()
35887     {
35888         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35889         
35890         var ts = new Roo.bootstrap.panel.Tabs({
35891                 el: this.bodyEl.dom,
35892                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35893                 disableTooltips: this.config.disableTabTips,
35894                 toolbar : this.config.toolbar
35895             });
35896         
35897         if(this.config.hideTabs){
35898             ts.stripWrap.setDisplayed(false);
35899         }
35900         this.tabs = ts;
35901         ts.resizeTabs = this.config.resizeTabs === true;
35902         ts.minTabWidth = this.config.minTabWidth || 40;
35903         ts.maxTabWidth = this.config.maxTabWidth || 250;
35904         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35905         ts.monitorResize = false;
35906         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35907         ts.bodyEl.addClass('roo-layout-tabs-body');
35908         this.panels.each(this.initPanelAsTab, this);
35909     },
35910
35911     initPanelAsTab : function(panel){
35912         var ti = this.tabs.addTab(
35913             panel.getEl().id,
35914             panel.getTitle(),
35915             null,
35916             this.config.closeOnTab && panel.isClosable(),
35917             panel.tpl
35918         );
35919         if(panel.tabTip !== undefined){
35920             ti.setTooltip(panel.tabTip);
35921         }
35922         ti.on("activate", function(){
35923               this.setActivePanel(panel);
35924         }, this);
35925         
35926         if(this.config.closeOnTab){
35927             ti.on("beforeclose", function(t, e){
35928                 e.cancel = true;
35929                 this.remove(panel);
35930             }, this);
35931         }
35932         
35933         panel.tabItem = ti;
35934         
35935         return ti;
35936     },
35937
35938     updatePanelTitle : function(panel, title)
35939     {
35940         if(this.activePanel == panel){
35941             this.updateTitle(title);
35942         }
35943         if(this.tabs){
35944             var ti = this.tabs.getTab(panel.getEl().id);
35945             ti.setText(title);
35946             if(panel.tabTip !== undefined){
35947                 ti.setTooltip(panel.tabTip);
35948             }
35949         }
35950     },
35951
35952     updateTitle : function(title){
35953         if(this.titleTextEl && !this.config.title){
35954             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35955         }
35956     },
35957
35958     setActivePanel : function(panel)
35959     {
35960         panel = this.getPanel(panel);
35961         if(this.activePanel && this.activePanel != panel){
35962             if(this.activePanel.setActiveState(false) === false){
35963                 return;
35964             }
35965         }
35966         this.activePanel = panel;
35967         panel.setActiveState(true);
35968         if(this.panelSize){
35969             panel.setSize(this.panelSize.width, this.panelSize.height);
35970         }
35971         if(this.closeBtn){
35972             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35973         }
35974         this.updateTitle(panel.getTitle());
35975         if(this.tabs){
35976             this.fireEvent("invalidated", this);
35977         }
35978         this.fireEvent("panelactivated", this, panel);
35979     },
35980
35981     /**
35982      * Shows the specified panel.
35983      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35984      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35985      */
35986     showPanel : function(panel)
35987     {
35988         panel = this.getPanel(panel);
35989         if(panel){
35990             if(this.tabs){
35991                 var tab = this.tabs.getTab(panel.getEl().id);
35992                 if(tab.isHidden()){
35993                     this.tabs.unhideTab(tab.id);
35994                 }
35995                 tab.activate();
35996             }else{
35997                 this.setActivePanel(panel);
35998             }
35999         }
36000         return panel;
36001     },
36002
36003     /**
36004      * Get the active panel for this region.
36005      * @return {Roo.ContentPanel} The active panel or null
36006      */
36007     getActivePanel : function(){
36008         return this.activePanel;
36009     },
36010
36011     validateVisibility : function(){
36012         if(this.panels.getCount() < 1){
36013             this.updateTitle("&#160;");
36014             this.closeBtn.hide();
36015             this.hide();
36016         }else{
36017             if(!this.isVisible()){
36018                 this.show();
36019             }
36020         }
36021     },
36022
36023     /**
36024      * Adds the passed ContentPanel(s) to this region.
36025      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36026      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36027      */
36028     add : function(panel)
36029     {
36030         if(arguments.length > 1){
36031             for(var i = 0, len = arguments.length; i < len; i++) {
36032                 this.add(arguments[i]);
36033             }
36034             return null;
36035         }
36036         
36037         // if we have not been rendered yet, then we can not really do much of this..
36038         if (!this.bodyEl) {
36039             this.unrendered_panels.push(panel);
36040             return panel;
36041         }
36042         
36043         
36044         
36045         
36046         if(this.hasPanel(panel)){
36047             this.showPanel(panel);
36048             return panel;
36049         }
36050         panel.setRegion(this);
36051         this.panels.add(panel);
36052        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36053             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36054             // and hide them... ???
36055             this.bodyEl.dom.appendChild(panel.getEl().dom);
36056             if(panel.background !== true){
36057                 this.setActivePanel(panel);
36058             }
36059             this.fireEvent("paneladded", this, panel);
36060             return panel;
36061         }
36062         */
36063         if(!this.tabs){
36064             this.initTabs();
36065         }else{
36066             this.initPanelAsTab(panel);
36067         }
36068         
36069         
36070         if(panel.background !== true){
36071             this.tabs.activate(panel.getEl().id);
36072         }
36073         this.fireEvent("paneladded", this, panel);
36074         return panel;
36075     },
36076
36077     /**
36078      * Hides the tab for the specified panel.
36079      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36080      */
36081     hidePanel : function(panel){
36082         if(this.tabs && (panel = this.getPanel(panel))){
36083             this.tabs.hideTab(panel.getEl().id);
36084         }
36085     },
36086
36087     /**
36088      * Unhides the tab for a previously hidden panel.
36089      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36090      */
36091     unhidePanel : function(panel){
36092         if(this.tabs && (panel = this.getPanel(panel))){
36093             this.tabs.unhideTab(panel.getEl().id);
36094         }
36095     },
36096
36097     clearPanels : function(){
36098         while(this.panels.getCount() > 0){
36099              this.remove(this.panels.first());
36100         }
36101     },
36102
36103     /**
36104      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36105      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36106      * @param {Boolean} preservePanel Overrides the config preservePanel option
36107      * @return {Roo.ContentPanel} The panel that was removed
36108      */
36109     remove : function(panel, preservePanel)
36110     {
36111         panel = this.getPanel(panel);
36112         if(!panel){
36113             return null;
36114         }
36115         var e = {};
36116         this.fireEvent("beforeremove", this, panel, e);
36117         if(e.cancel === true){
36118             return null;
36119         }
36120         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36121         var panelId = panel.getId();
36122         this.panels.removeKey(panelId);
36123         if(preservePanel){
36124             document.body.appendChild(panel.getEl().dom);
36125         }
36126         if(this.tabs){
36127             this.tabs.removeTab(panel.getEl().id);
36128         }else if (!preservePanel){
36129             this.bodyEl.dom.removeChild(panel.getEl().dom);
36130         }
36131         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36132             var p = this.panels.first();
36133             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36134             tempEl.appendChild(p.getEl().dom);
36135             this.bodyEl.update("");
36136             this.bodyEl.dom.appendChild(p.getEl().dom);
36137             tempEl = null;
36138             this.updateTitle(p.getTitle());
36139             this.tabs = null;
36140             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36141             this.setActivePanel(p);
36142         }
36143         panel.setRegion(null);
36144         if(this.activePanel == panel){
36145             this.activePanel = null;
36146         }
36147         if(this.config.autoDestroy !== false && preservePanel !== true){
36148             try{panel.destroy();}catch(e){}
36149         }
36150         this.fireEvent("panelremoved", this, panel);
36151         return panel;
36152     },
36153
36154     /**
36155      * Returns the TabPanel component used by this region
36156      * @return {Roo.TabPanel}
36157      */
36158     getTabs : function(){
36159         return this.tabs;
36160     },
36161
36162     createTool : function(parentEl, className){
36163         var btn = Roo.DomHelper.append(parentEl, {
36164             tag: "div",
36165             cls: "x-layout-tools-button",
36166             children: [ {
36167                 tag: "div",
36168                 cls: "roo-layout-tools-button-inner " + className,
36169                 html: "&#160;"
36170             }]
36171         }, true);
36172         btn.addClassOnOver("roo-layout-tools-button-over");
36173         return btn;
36174     }
36175 });/*
36176  * Based on:
36177  * Ext JS Library 1.1.1
36178  * Copyright(c) 2006-2007, Ext JS, LLC.
36179  *
36180  * Originally Released Under LGPL - original licence link has changed is not relivant.
36181  *
36182  * Fork - LGPL
36183  * <script type="text/javascript">
36184  */
36185  
36186
36187
36188 /**
36189  * @class Roo.SplitLayoutRegion
36190  * @extends Roo.LayoutRegion
36191  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36192  */
36193 Roo.bootstrap.layout.Split = function(config){
36194     this.cursor = config.cursor;
36195     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36196 };
36197
36198 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36199 {
36200     splitTip : "Drag to resize.",
36201     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36202     useSplitTips : false,
36203
36204     applyConfig : function(config){
36205         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36206     },
36207     
36208     onRender : function(ctr,pos) {
36209         
36210         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36211         if(!this.config.split){
36212             return;
36213         }
36214         if(!this.split){
36215             
36216             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36217                             tag: "div",
36218                             id: this.el.id + "-split",
36219                             cls: "roo-layout-split roo-layout-split-"+this.position,
36220                             html: "&#160;"
36221             });
36222             /** The SplitBar for this region 
36223             * @type Roo.SplitBar */
36224             // does not exist yet...
36225             Roo.log([this.position, this.orientation]);
36226             
36227             this.split = new Roo.bootstrap.SplitBar({
36228                 dragElement : splitEl,
36229                 resizingElement: this.el,
36230                 orientation : this.orientation
36231             });
36232             
36233             this.split.on("moved", this.onSplitMove, this);
36234             this.split.useShim = this.config.useShim === true;
36235             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36236             if(this.useSplitTips){
36237                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36238             }
36239             //if(config.collapsible){
36240             //    this.split.el.on("dblclick", this.collapse,  this);
36241             //}
36242         }
36243         if(typeof this.config.minSize != "undefined"){
36244             this.split.minSize = this.config.minSize;
36245         }
36246         if(typeof this.config.maxSize != "undefined"){
36247             this.split.maxSize = this.config.maxSize;
36248         }
36249         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36250             this.hideSplitter();
36251         }
36252         
36253     },
36254
36255     getHMaxSize : function(){
36256          var cmax = this.config.maxSize || 10000;
36257          var center = this.mgr.getRegion("center");
36258          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36259     },
36260
36261     getVMaxSize : function(){
36262          var cmax = this.config.maxSize || 10000;
36263          var center = this.mgr.getRegion("center");
36264          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36265     },
36266
36267     onSplitMove : function(split, newSize){
36268         this.fireEvent("resized", this, newSize);
36269     },
36270     
36271     /** 
36272      * Returns the {@link Roo.SplitBar} for this region.
36273      * @return {Roo.SplitBar}
36274      */
36275     getSplitBar : function(){
36276         return this.split;
36277     },
36278     
36279     hide : function(){
36280         this.hideSplitter();
36281         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36282     },
36283
36284     hideSplitter : function(){
36285         if(this.split){
36286             this.split.el.setLocation(-2000,-2000);
36287             this.split.el.hide();
36288         }
36289     },
36290
36291     show : function(){
36292         if(this.split){
36293             this.split.el.show();
36294         }
36295         Roo.bootstrap.layout.Split.superclass.show.call(this);
36296     },
36297     
36298     beforeSlide: function(){
36299         if(Roo.isGecko){// firefox overflow auto bug workaround
36300             this.bodyEl.clip();
36301             if(this.tabs) {
36302                 this.tabs.bodyEl.clip();
36303             }
36304             if(this.activePanel){
36305                 this.activePanel.getEl().clip();
36306                 
36307                 if(this.activePanel.beforeSlide){
36308                     this.activePanel.beforeSlide();
36309                 }
36310             }
36311         }
36312     },
36313     
36314     afterSlide : function(){
36315         if(Roo.isGecko){// firefox overflow auto bug workaround
36316             this.bodyEl.unclip();
36317             if(this.tabs) {
36318                 this.tabs.bodyEl.unclip();
36319             }
36320             if(this.activePanel){
36321                 this.activePanel.getEl().unclip();
36322                 if(this.activePanel.afterSlide){
36323                     this.activePanel.afterSlide();
36324                 }
36325             }
36326         }
36327     },
36328
36329     initAutoHide : function(){
36330         if(this.autoHide !== false){
36331             if(!this.autoHideHd){
36332                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36333                 this.autoHideHd = {
36334                     "mouseout": function(e){
36335                         if(!e.within(this.el, true)){
36336                             st.delay(500);
36337                         }
36338                     },
36339                     "mouseover" : function(e){
36340                         st.cancel();
36341                     },
36342                     scope : this
36343                 };
36344             }
36345             this.el.on(this.autoHideHd);
36346         }
36347     },
36348
36349     clearAutoHide : function(){
36350         if(this.autoHide !== false){
36351             this.el.un("mouseout", this.autoHideHd.mouseout);
36352             this.el.un("mouseover", this.autoHideHd.mouseover);
36353         }
36354     },
36355
36356     clearMonitor : function(){
36357         Roo.get(document).un("click", this.slideInIf, this);
36358     },
36359
36360     // these names are backwards but not changed for compat
36361     slideOut : function(){
36362         if(this.isSlid || this.el.hasActiveFx()){
36363             return;
36364         }
36365         this.isSlid = true;
36366         if(this.collapseBtn){
36367             this.collapseBtn.hide();
36368         }
36369         this.closeBtnState = this.closeBtn.getStyle('display');
36370         this.closeBtn.hide();
36371         if(this.stickBtn){
36372             this.stickBtn.show();
36373         }
36374         this.el.show();
36375         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36376         this.beforeSlide();
36377         this.el.setStyle("z-index", 10001);
36378         this.el.slideIn(this.getSlideAnchor(), {
36379             callback: function(){
36380                 this.afterSlide();
36381                 this.initAutoHide();
36382                 Roo.get(document).on("click", this.slideInIf, this);
36383                 this.fireEvent("slideshow", this);
36384             },
36385             scope: this,
36386             block: true
36387         });
36388     },
36389
36390     afterSlideIn : function(){
36391         this.clearAutoHide();
36392         this.isSlid = false;
36393         this.clearMonitor();
36394         this.el.setStyle("z-index", "");
36395         if(this.collapseBtn){
36396             this.collapseBtn.show();
36397         }
36398         this.closeBtn.setStyle('display', this.closeBtnState);
36399         if(this.stickBtn){
36400             this.stickBtn.hide();
36401         }
36402         this.fireEvent("slidehide", this);
36403     },
36404
36405     slideIn : function(cb){
36406         if(!this.isSlid || this.el.hasActiveFx()){
36407             Roo.callback(cb);
36408             return;
36409         }
36410         this.isSlid = false;
36411         this.beforeSlide();
36412         this.el.slideOut(this.getSlideAnchor(), {
36413             callback: function(){
36414                 this.el.setLeftTop(-10000, -10000);
36415                 this.afterSlide();
36416                 this.afterSlideIn();
36417                 Roo.callback(cb);
36418             },
36419             scope: this,
36420             block: true
36421         });
36422     },
36423     
36424     slideInIf : function(e){
36425         if(!e.within(this.el)){
36426             this.slideIn();
36427         }
36428     },
36429
36430     animateCollapse : function(){
36431         this.beforeSlide();
36432         this.el.setStyle("z-index", 20000);
36433         var anchor = this.getSlideAnchor();
36434         this.el.slideOut(anchor, {
36435             callback : function(){
36436                 this.el.setStyle("z-index", "");
36437                 this.collapsedEl.slideIn(anchor, {duration:.3});
36438                 this.afterSlide();
36439                 this.el.setLocation(-10000,-10000);
36440                 this.el.hide();
36441                 this.fireEvent("collapsed", this);
36442             },
36443             scope: this,
36444             block: true
36445         });
36446     },
36447
36448     animateExpand : function(){
36449         this.beforeSlide();
36450         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36451         this.el.setStyle("z-index", 20000);
36452         this.collapsedEl.hide({
36453             duration:.1
36454         });
36455         this.el.slideIn(this.getSlideAnchor(), {
36456             callback : function(){
36457                 this.el.setStyle("z-index", "");
36458                 this.afterSlide();
36459                 if(this.split){
36460                     this.split.el.show();
36461                 }
36462                 this.fireEvent("invalidated", this);
36463                 this.fireEvent("expanded", this);
36464             },
36465             scope: this,
36466             block: true
36467         });
36468     },
36469
36470     anchors : {
36471         "west" : "left",
36472         "east" : "right",
36473         "north" : "top",
36474         "south" : "bottom"
36475     },
36476
36477     sanchors : {
36478         "west" : "l",
36479         "east" : "r",
36480         "north" : "t",
36481         "south" : "b"
36482     },
36483
36484     canchors : {
36485         "west" : "tl-tr",
36486         "east" : "tr-tl",
36487         "north" : "tl-bl",
36488         "south" : "bl-tl"
36489     },
36490
36491     getAnchor : function(){
36492         return this.anchors[this.position];
36493     },
36494
36495     getCollapseAnchor : function(){
36496         return this.canchors[this.position];
36497     },
36498
36499     getSlideAnchor : function(){
36500         return this.sanchors[this.position];
36501     },
36502
36503     getAlignAdj : function(){
36504         var cm = this.cmargins;
36505         switch(this.position){
36506             case "west":
36507                 return [0, 0];
36508             break;
36509             case "east":
36510                 return [0, 0];
36511             break;
36512             case "north":
36513                 return [0, 0];
36514             break;
36515             case "south":
36516                 return [0, 0];
36517             break;
36518         }
36519     },
36520
36521     getExpandAdj : function(){
36522         var c = this.collapsedEl, cm = this.cmargins;
36523         switch(this.position){
36524             case "west":
36525                 return [-(cm.right+c.getWidth()+cm.left), 0];
36526             break;
36527             case "east":
36528                 return [cm.right+c.getWidth()+cm.left, 0];
36529             break;
36530             case "north":
36531                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36532             break;
36533             case "south":
36534                 return [0, cm.top+cm.bottom+c.getHeight()];
36535             break;
36536         }
36537     }
36538 });/*
36539  * Based on:
36540  * Ext JS Library 1.1.1
36541  * Copyright(c) 2006-2007, Ext JS, LLC.
36542  *
36543  * Originally Released Under LGPL - original licence link has changed is not relivant.
36544  *
36545  * Fork - LGPL
36546  * <script type="text/javascript">
36547  */
36548 /*
36549  * These classes are private internal classes
36550  */
36551 Roo.bootstrap.layout.Center = function(config){
36552     config.region = "center";
36553     Roo.bootstrap.layout.Region.call(this, config);
36554     this.visible = true;
36555     this.minWidth = config.minWidth || 20;
36556     this.minHeight = config.minHeight || 20;
36557 };
36558
36559 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36560     hide : function(){
36561         // center panel can't be hidden
36562     },
36563     
36564     show : function(){
36565         // center panel can't be hidden
36566     },
36567     
36568     getMinWidth: function(){
36569         return this.minWidth;
36570     },
36571     
36572     getMinHeight: function(){
36573         return this.minHeight;
36574     }
36575 });
36576
36577
36578
36579
36580  
36581
36582
36583
36584
36585
36586 Roo.bootstrap.layout.North = function(config)
36587 {
36588     config.region = 'north';
36589     config.cursor = 'n-resize';
36590     
36591     Roo.bootstrap.layout.Split.call(this, config);
36592     
36593     
36594     if(this.split){
36595         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36596         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36597         this.split.el.addClass("roo-layout-split-v");
36598     }
36599     var size = config.initialSize || config.height;
36600     if(typeof size != "undefined"){
36601         this.el.setHeight(size);
36602     }
36603 };
36604 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36605 {
36606     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36607     
36608     
36609     
36610     getBox : function(){
36611         if(this.collapsed){
36612             return this.collapsedEl.getBox();
36613         }
36614         var box = this.el.getBox();
36615         if(this.split){
36616             box.height += this.split.el.getHeight();
36617         }
36618         return box;
36619     },
36620     
36621     updateBox : function(box){
36622         if(this.split && !this.collapsed){
36623             box.height -= this.split.el.getHeight();
36624             this.split.el.setLeft(box.x);
36625             this.split.el.setTop(box.y+box.height);
36626             this.split.el.setWidth(box.width);
36627         }
36628         if(this.collapsed){
36629             this.updateBody(box.width, null);
36630         }
36631         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36632     }
36633 });
36634
36635
36636
36637
36638
36639 Roo.bootstrap.layout.South = function(config){
36640     config.region = 'south';
36641     config.cursor = 's-resize';
36642     Roo.bootstrap.layout.Split.call(this, config);
36643     if(this.split){
36644         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36645         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36646         this.split.el.addClass("roo-layout-split-v");
36647     }
36648     var size = config.initialSize || config.height;
36649     if(typeof size != "undefined"){
36650         this.el.setHeight(size);
36651     }
36652 };
36653
36654 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36655     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36656     getBox : function(){
36657         if(this.collapsed){
36658             return this.collapsedEl.getBox();
36659         }
36660         var box = this.el.getBox();
36661         if(this.split){
36662             var sh = this.split.el.getHeight();
36663             box.height += sh;
36664             box.y -= sh;
36665         }
36666         return box;
36667     },
36668     
36669     updateBox : function(box){
36670         if(this.split && !this.collapsed){
36671             var sh = this.split.el.getHeight();
36672             box.height -= sh;
36673             box.y += sh;
36674             this.split.el.setLeft(box.x);
36675             this.split.el.setTop(box.y-sh);
36676             this.split.el.setWidth(box.width);
36677         }
36678         if(this.collapsed){
36679             this.updateBody(box.width, null);
36680         }
36681         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36682     }
36683 });
36684
36685 Roo.bootstrap.layout.East = function(config){
36686     config.region = "east";
36687     config.cursor = "e-resize";
36688     Roo.bootstrap.layout.Split.call(this, config);
36689     if(this.split){
36690         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36691         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36692         this.split.el.addClass("roo-layout-split-h");
36693     }
36694     var size = config.initialSize || config.width;
36695     if(typeof size != "undefined"){
36696         this.el.setWidth(size);
36697     }
36698 };
36699 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36700     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36701     getBox : function(){
36702         if(this.collapsed){
36703             return this.collapsedEl.getBox();
36704         }
36705         var box = this.el.getBox();
36706         if(this.split){
36707             var sw = this.split.el.getWidth();
36708             box.width += sw;
36709             box.x -= sw;
36710         }
36711         return box;
36712     },
36713
36714     updateBox : function(box){
36715         if(this.split && !this.collapsed){
36716             var sw = this.split.el.getWidth();
36717             box.width -= sw;
36718             this.split.el.setLeft(box.x);
36719             this.split.el.setTop(box.y);
36720             this.split.el.setHeight(box.height);
36721             box.x += sw;
36722         }
36723         if(this.collapsed){
36724             this.updateBody(null, box.height);
36725         }
36726         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36727     }
36728 });
36729
36730 Roo.bootstrap.layout.West = function(config){
36731     config.region = "west";
36732     config.cursor = "w-resize";
36733     
36734     Roo.bootstrap.layout.Split.call(this, config);
36735     if(this.split){
36736         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36737         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36738         this.split.el.addClass("roo-layout-split-h");
36739     }
36740     
36741 };
36742 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36743     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36744     
36745     onRender: function(ctr, pos)
36746     {
36747         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36748         var size = this.config.initialSize || this.config.width;
36749         if(typeof size != "undefined"){
36750             this.el.setWidth(size);
36751         }
36752     },
36753     
36754     getBox : function(){
36755         if(this.collapsed){
36756             return this.collapsedEl.getBox();
36757         }
36758         var box = this.el.getBox();
36759         if(this.split){
36760             box.width += this.split.el.getWidth();
36761         }
36762         return box;
36763     },
36764     
36765     updateBox : function(box){
36766         if(this.split && !this.collapsed){
36767             var sw = this.split.el.getWidth();
36768             box.width -= sw;
36769             this.split.el.setLeft(box.x+box.width);
36770             this.split.el.setTop(box.y);
36771             this.split.el.setHeight(box.height);
36772         }
36773         if(this.collapsed){
36774             this.updateBody(null, box.height);
36775         }
36776         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36777     }
36778 });
36779 Roo.namespace("Roo.bootstrap.panel");/*
36780  * Based on:
36781  * Ext JS Library 1.1.1
36782  * Copyright(c) 2006-2007, Ext JS, LLC.
36783  *
36784  * Originally Released Under LGPL - original licence link has changed is not relivant.
36785  *
36786  * Fork - LGPL
36787  * <script type="text/javascript">
36788  */
36789 /**
36790  * @class Roo.ContentPanel
36791  * @extends Roo.util.Observable
36792  * A basic ContentPanel element.
36793  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36794  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36795  * @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
36796  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36797  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36798  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36799  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36800  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36801  * @cfg {String} title          The title for this panel
36802  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36803  * @cfg {String} url            Calls {@link #setUrl} with this value
36804  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36805  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36806  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36807  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36808  * @cfg {Boolean} badges render the badges
36809
36810  * @constructor
36811  * Create a new ContentPanel.
36812  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36813  * @param {String/Object} config A string to set only the title or a config object
36814  * @param {String} content (optional) Set the HTML content for this panel
36815  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36816  */
36817 Roo.bootstrap.panel.Content = function( config){
36818     
36819     this.tpl = config.tpl || false;
36820     
36821     var el = config.el;
36822     var content = config.content;
36823
36824     if(config.autoCreate){ // xtype is available if this is called from factory
36825         el = Roo.id();
36826     }
36827     this.el = Roo.get(el);
36828     if(!this.el && config && config.autoCreate){
36829         if(typeof config.autoCreate == "object"){
36830             if(!config.autoCreate.id){
36831                 config.autoCreate.id = config.id||el;
36832             }
36833             this.el = Roo.DomHelper.append(document.body,
36834                         config.autoCreate, true);
36835         }else{
36836             var elcfg =  {   tag: "div",
36837                             cls: "roo-layout-inactive-content",
36838                             id: config.id||el
36839                             };
36840             if (config.html) {
36841                 elcfg.html = config.html;
36842                 
36843             }
36844                         
36845             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36846         }
36847     } 
36848     this.closable = false;
36849     this.loaded = false;
36850     this.active = false;
36851    
36852       
36853     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36854         
36855         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36856         
36857         this.wrapEl = this.el; //this.el.wrap();
36858         var ti = [];
36859         if (config.toolbar.items) {
36860             ti = config.toolbar.items ;
36861             delete config.toolbar.items ;
36862         }
36863         
36864         var nitems = [];
36865         this.toolbar.render(this.wrapEl, 'before');
36866         for(var i =0;i < ti.length;i++) {
36867           //  Roo.log(['add child', items[i]]);
36868             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36869         }
36870         this.toolbar.items = nitems;
36871         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36872         delete config.toolbar;
36873         
36874     }
36875     /*
36876     // xtype created footer. - not sure if will work as we normally have to render first..
36877     if (this.footer && !this.footer.el && this.footer.xtype) {
36878         if (!this.wrapEl) {
36879             this.wrapEl = this.el.wrap();
36880         }
36881     
36882         this.footer.container = this.wrapEl.createChild();
36883          
36884         this.footer = Roo.factory(this.footer, Roo);
36885         
36886     }
36887     */
36888     
36889      if(typeof config == "string"){
36890         this.title = config;
36891     }else{
36892         Roo.apply(this, config);
36893     }
36894     
36895     if(this.resizeEl){
36896         this.resizeEl = Roo.get(this.resizeEl, true);
36897     }else{
36898         this.resizeEl = this.el;
36899     }
36900     // handle view.xtype
36901     
36902  
36903     
36904     
36905     this.addEvents({
36906         /**
36907          * @event activate
36908          * Fires when this panel is activated. 
36909          * @param {Roo.ContentPanel} this
36910          */
36911         "activate" : true,
36912         /**
36913          * @event deactivate
36914          * Fires when this panel is activated. 
36915          * @param {Roo.ContentPanel} this
36916          */
36917         "deactivate" : true,
36918
36919         /**
36920          * @event resize
36921          * Fires when this panel is resized if fitToFrame is true.
36922          * @param {Roo.ContentPanel} this
36923          * @param {Number} width The width after any component adjustments
36924          * @param {Number} height The height after any component adjustments
36925          */
36926         "resize" : true,
36927         
36928          /**
36929          * @event render
36930          * Fires when this tab is created
36931          * @param {Roo.ContentPanel} this
36932          */
36933         "render" : true
36934         
36935         
36936         
36937     });
36938     
36939
36940     
36941     
36942     if(this.autoScroll){
36943         this.resizeEl.setStyle("overflow", "auto");
36944     } else {
36945         // fix randome scrolling
36946         //this.el.on('scroll', function() {
36947         //    Roo.log('fix random scolling');
36948         //    this.scrollTo('top',0); 
36949         //});
36950     }
36951     content = content || this.content;
36952     if(content){
36953         this.setContent(content);
36954     }
36955     if(config && config.url){
36956         this.setUrl(this.url, this.params, this.loadOnce);
36957     }
36958     
36959     
36960     
36961     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36962     
36963     if (this.view && typeof(this.view.xtype) != 'undefined') {
36964         this.view.el = this.el.appendChild(document.createElement("div"));
36965         this.view = Roo.factory(this.view); 
36966         this.view.render  &&  this.view.render(false, '');  
36967     }
36968     
36969     
36970     this.fireEvent('render', this);
36971 };
36972
36973 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36974     
36975     tabTip : '',
36976     
36977     setRegion : function(region){
36978         this.region = region;
36979         this.setActiveClass(region && !this.background);
36980     },
36981     
36982     
36983     setActiveClass: function(state)
36984     {
36985         if(state){
36986            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36987            this.el.setStyle('position','relative');
36988         }else{
36989            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36990            this.el.setStyle('position', 'absolute');
36991         } 
36992     },
36993     
36994     /**
36995      * Returns the toolbar for this Panel if one was configured. 
36996      * @return {Roo.Toolbar} 
36997      */
36998     getToolbar : function(){
36999         return this.toolbar;
37000     },
37001     
37002     setActiveState : function(active)
37003     {
37004         this.active = active;
37005         this.setActiveClass(active);
37006         if(!active){
37007             if(this.fireEvent("deactivate", this) === false){
37008                 return false;
37009             }
37010             return true;
37011         }
37012         this.fireEvent("activate", this);
37013         return true;
37014     },
37015     /**
37016      * Updates this panel's element
37017      * @param {String} content The new content
37018      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37019     */
37020     setContent : function(content, loadScripts){
37021         this.el.update(content, loadScripts);
37022     },
37023
37024     ignoreResize : function(w, h){
37025         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37026             return true;
37027         }else{
37028             this.lastSize = {width: w, height: h};
37029             return false;
37030         }
37031     },
37032     /**
37033      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37034      * @return {Roo.UpdateManager} The UpdateManager
37035      */
37036     getUpdateManager : function(){
37037         return this.el.getUpdateManager();
37038     },
37039      /**
37040      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37041      * @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:
37042 <pre><code>
37043 panel.load({
37044     url: "your-url.php",
37045     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37046     callback: yourFunction,
37047     scope: yourObject, //(optional scope)
37048     discardUrl: false,
37049     nocache: false,
37050     text: "Loading...",
37051     timeout: 30,
37052     scripts: false
37053 });
37054 </code></pre>
37055      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37056      * 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.
37057      * @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}
37058      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37059      * @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.
37060      * @return {Roo.ContentPanel} this
37061      */
37062     load : function(){
37063         var um = this.el.getUpdateManager();
37064         um.update.apply(um, arguments);
37065         return this;
37066     },
37067
37068
37069     /**
37070      * 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.
37071      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37072      * @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)
37073      * @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)
37074      * @return {Roo.UpdateManager} The UpdateManager
37075      */
37076     setUrl : function(url, params, loadOnce){
37077         if(this.refreshDelegate){
37078             this.removeListener("activate", this.refreshDelegate);
37079         }
37080         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37081         this.on("activate", this.refreshDelegate);
37082         return this.el.getUpdateManager();
37083     },
37084     
37085     _handleRefresh : function(url, params, loadOnce){
37086         if(!loadOnce || !this.loaded){
37087             var updater = this.el.getUpdateManager();
37088             updater.update(url, params, this._setLoaded.createDelegate(this));
37089         }
37090     },
37091     
37092     _setLoaded : function(){
37093         this.loaded = true;
37094     }, 
37095     
37096     /**
37097      * Returns this panel's id
37098      * @return {String} 
37099      */
37100     getId : function(){
37101         return this.el.id;
37102     },
37103     
37104     /** 
37105      * Returns this panel's element - used by regiosn to add.
37106      * @return {Roo.Element} 
37107      */
37108     getEl : function(){
37109         return this.wrapEl || this.el;
37110     },
37111     
37112    
37113     
37114     adjustForComponents : function(width, height)
37115     {
37116         //Roo.log('adjustForComponents ');
37117         if(this.resizeEl != this.el){
37118             width -= this.el.getFrameWidth('lr');
37119             height -= this.el.getFrameWidth('tb');
37120         }
37121         if(this.toolbar){
37122             var te = this.toolbar.getEl();
37123             te.setWidth(width);
37124             height -= te.getHeight();
37125         }
37126         if(this.footer){
37127             var te = this.footer.getEl();
37128             te.setWidth(width);
37129             height -= te.getHeight();
37130         }
37131         
37132         
37133         if(this.adjustments){
37134             width += this.adjustments[0];
37135             height += this.adjustments[1];
37136         }
37137         return {"width": width, "height": height};
37138     },
37139     
37140     setSize : function(width, height){
37141         if(this.fitToFrame && !this.ignoreResize(width, height)){
37142             if(this.fitContainer && this.resizeEl != this.el){
37143                 this.el.setSize(width, height);
37144             }
37145             var size = this.adjustForComponents(width, height);
37146             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37147             this.fireEvent('resize', this, size.width, size.height);
37148         }
37149     },
37150     
37151     /**
37152      * Returns this panel's title
37153      * @return {String} 
37154      */
37155     getTitle : function(){
37156         
37157         if (typeof(this.title) != 'object') {
37158             return this.title;
37159         }
37160         
37161         var t = '';
37162         for (var k in this.title) {
37163             if (!this.title.hasOwnProperty(k)) {
37164                 continue;
37165             }
37166             
37167             if (k.indexOf('-') >= 0) {
37168                 var s = k.split('-');
37169                 for (var i = 0; i<s.length; i++) {
37170                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37171                 }
37172             } else {
37173                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37174             }
37175         }
37176         return t;
37177     },
37178     
37179     /**
37180      * Set this panel's title
37181      * @param {String} title
37182      */
37183     setTitle : function(title){
37184         this.title = title;
37185         if(this.region){
37186             this.region.updatePanelTitle(this, title);
37187         }
37188     },
37189     
37190     /**
37191      * Returns true is this panel was configured to be closable
37192      * @return {Boolean} 
37193      */
37194     isClosable : function(){
37195         return this.closable;
37196     },
37197     
37198     beforeSlide : function(){
37199         this.el.clip();
37200         this.resizeEl.clip();
37201     },
37202     
37203     afterSlide : function(){
37204         this.el.unclip();
37205         this.resizeEl.unclip();
37206     },
37207     
37208     /**
37209      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37210      *   Will fail silently if the {@link #setUrl} method has not been called.
37211      *   This does not activate the panel, just updates its content.
37212      */
37213     refresh : function(){
37214         if(this.refreshDelegate){
37215            this.loaded = false;
37216            this.refreshDelegate();
37217         }
37218     },
37219     
37220     /**
37221      * Destroys this panel
37222      */
37223     destroy : function(){
37224         this.el.removeAllListeners();
37225         var tempEl = document.createElement("span");
37226         tempEl.appendChild(this.el.dom);
37227         tempEl.innerHTML = "";
37228         this.el.remove();
37229         this.el = null;
37230     },
37231     
37232     /**
37233      * form - if the content panel contains a form - this is a reference to it.
37234      * @type {Roo.form.Form}
37235      */
37236     form : false,
37237     /**
37238      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37239      *    This contains a reference to it.
37240      * @type {Roo.View}
37241      */
37242     view : false,
37243     
37244       /**
37245      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37246      * <pre><code>
37247
37248 layout.addxtype({
37249        xtype : 'Form',
37250        items: [ .... ]
37251    }
37252 );
37253
37254 </code></pre>
37255      * @param {Object} cfg Xtype definition of item to add.
37256      */
37257     
37258     
37259     getChildContainer: function () {
37260         return this.getEl();
37261     }
37262     
37263     
37264     /*
37265         var  ret = new Roo.factory(cfg);
37266         return ret;
37267         
37268         
37269         // add form..
37270         if (cfg.xtype.match(/^Form$/)) {
37271             
37272             var el;
37273             //if (this.footer) {
37274             //    el = this.footer.container.insertSibling(false, 'before');
37275             //} else {
37276                 el = this.el.createChild();
37277             //}
37278
37279             this.form = new  Roo.form.Form(cfg);
37280             
37281             
37282             if ( this.form.allItems.length) {
37283                 this.form.render(el.dom);
37284             }
37285             return this.form;
37286         }
37287         // should only have one of theses..
37288         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37289             // views.. should not be just added - used named prop 'view''
37290             
37291             cfg.el = this.el.appendChild(document.createElement("div"));
37292             // factory?
37293             
37294             var ret = new Roo.factory(cfg);
37295              
37296              ret.render && ret.render(false, ''); // render blank..
37297             this.view = ret;
37298             return ret;
37299         }
37300         return false;
37301     }
37302     \*/
37303 });
37304  
37305 /**
37306  * @class Roo.bootstrap.panel.Grid
37307  * @extends Roo.bootstrap.panel.Content
37308  * @constructor
37309  * Create a new GridPanel.
37310  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37311  * @param {Object} config A the config object
37312   
37313  */
37314
37315
37316
37317 Roo.bootstrap.panel.Grid = function(config)
37318 {
37319     
37320       
37321     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37322         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37323
37324     config.el = this.wrapper;
37325     //this.el = this.wrapper;
37326     
37327       if (config.container) {
37328         // ctor'ed from a Border/panel.grid
37329         
37330         
37331         this.wrapper.setStyle("overflow", "hidden");
37332         this.wrapper.addClass('roo-grid-container');
37333
37334     }
37335     
37336     
37337     if(config.toolbar){
37338         var tool_el = this.wrapper.createChild();    
37339         this.toolbar = Roo.factory(config.toolbar);
37340         var ti = [];
37341         if (config.toolbar.items) {
37342             ti = config.toolbar.items ;
37343             delete config.toolbar.items ;
37344         }
37345         
37346         var nitems = [];
37347         this.toolbar.render(tool_el);
37348         for(var i =0;i < ti.length;i++) {
37349           //  Roo.log(['add child', items[i]]);
37350             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37351         }
37352         this.toolbar.items = nitems;
37353         
37354         delete config.toolbar;
37355     }
37356     
37357     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37358     config.grid.scrollBody = true;;
37359     config.grid.monitorWindowResize = false; // turn off autosizing
37360     config.grid.autoHeight = false;
37361     config.grid.autoWidth = false;
37362     
37363     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37364     
37365     if (config.background) {
37366         // render grid on panel activation (if panel background)
37367         this.on('activate', function(gp) {
37368             if (!gp.grid.rendered) {
37369                 gp.grid.render(this.wrapper);
37370                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37371             }
37372         });
37373             
37374     } else {
37375         this.grid.render(this.wrapper);
37376         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37377
37378     }
37379     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37380     // ??? needed ??? config.el = this.wrapper;
37381     
37382     
37383     
37384   
37385     // xtype created footer. - not sure if will work as we normally have to render first..
37386     if (this.footer && !this.footer.el && this.footer.xtype) {
37387         
37388         var ctr = this.grid.getView().getFooterPanel(true);
37389         this.footer.dataSource = this.grid.dataSource;
37390         this.footer = Roo.factory(this.footer, Roo);
37391         this.footer.render(ctr);
37392         
37393     }
37394     
37395     
37396     
37397     
37398      
37399 };
37400
37401 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37402     getId : function(){
37403         return this.grid.id;
37404     },
37405     
37406     /**
37407      * Returns the grid for this panel
37408      * @return {Roo.bootstrap.Table} 
37409      */
37410     getGrid : function(){
37411         return this.grid;    
37412     },
37413     
37414     setSize : function(width, height){
37415         if(!this.ignoreResize(width, height)){
37416             var grid = this.grid;
37417             var size = this.adjustForComponents(width, height);
37418             var gridel = grid.getGridEl();
37419             gridel.setSize(size.width, size.height);
37420             /*
37421             var thd = grid.getGridEl().select('thead',true).first();
37422             var tbd = grid.getGridEl().select('tbody', true).first();
37423             if (tbd) {
37424                 tbd.setSize(width, height - thd.getHeight());
37425             }
37426             */
37427             grid.autoSize();
37428         }
37429     },
37430      
37431     
37432     
37433     beforeSlide : function(){
37434         this.grid.getView().scroller.clip();
37435     },
37436     
37437     afterSlide : function(){
37438         this.grid.getView().scroller.unclip();
37439     },
37440     
37441     destroy : function(){
37442         this.grid.destroy();
37443         delete this.grid;
37444         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37445     }
37446 });
37447
37448 /**
37449  * @class Roo.bootstrap.panel.Nest
37450  * @extends Roo.bootstrap.panel.Content
37451  * @constructor
37452  * Create a new Panel, that can contain a layout.Border.
37453  * 
37454  * 
37455  * @param {Roo.BorderLayout} layout The layout for this panel
37456  * @param {String/Object} config A string to set only the title or a config object
37457  */
37458 Roo.bootstrap.panel.Nest = function(config)
37459 {
37460     // construct with only one argument..
37461     /* FIXME - implement nicer consturctors
37462     if (layout.layout) {
37463         config = layout;
37464         layout = config.layout;
37465         delete config.layout;
37466     }
37467     if (layout.xtype && !layout.getEl) {
37468         // then layout needs constructing..
37469         layout = Roo.factory(layout, Roo);
37470     }
37471     */
37472     
37473     config.el =  config.layout.getEl();
37474     
37475     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37476     
37477     config.layout.monitorWindowResize = false; // turn off autosizing
37478     this.layout = config.layout;
37479     this.layout.getEl().addClass("roo-layout-nested-layout");
37480     
37481     
37482     
37483     
37484 };
37485
37486 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37487
37488     setSize : function(width, height){
37489         if(!this.ignoreResize(width, height)){
37490             var size = this.adjustForComponents(width, height);
37491             var el = this.layout.getEl();
37492             if (size.height < 1) {
37493                 el.setWidth(size.width);   
37494             } else {
37495                 el.setSize(size.width, size.height);
37496             }
37497             var touch = el.dom.offsetWidth;
37498             this.layout.layout();
37499             // ie requires a double layout on the first pass
37500             if(Roo.isIE && !this.initialized){
37501                 this.initialized = true;
37502                 this.layout.layout();
37503             }
37504         }
37505     },
37506     
37507     // activate all subpanels if not currently active..
37508     
37509     setActiveState : function(active){
37510         this.active = active;
37511         this.setActiveClass(active);
37512         
37513         if(!active){
37514             this.fireEvent("deactivate", this);
37515             return;
37516         }
37517         
37518         this.fireEvent("activate", this);
37519         // not sure if this should happen before or after..
37520         if (!this.layout) {
37521             return; // should not happen..
37522         }
37523         var reg = false;
37524         for (var r in this.layout.regions) {
37525             reg = this.layout.getRegion(r);
37526             if (reg.getActivePanel()) {
37527                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37528                 reg.setActivePanel(reg.getActivePanel());
37529                 continue;
37530             }
37531             if (!reg.panels.length) {
37532                 continue;
37533             }
37534             reg.showPanel(reg.getPanel(0));
37535         }
37536         
37537         
37538         
37539         
37540     },
37541     
37542     /**
37543      * Returns the nested BorderLayout for this panel
37544      * @return {Roo.BorderLayout} 
37545      */
37546     getLayout : function(){
37547         return this.layout;
37548     },
37549     
37550      /**
37551      * Adds a xtype elements to the layout of the nested panel
37552      * <pre><code>
37553
37554 panel.addxtype({
37555        xtype : 'ContentPanel',
37556        region: 'west',
37557        items: [ .... ]
37558    }
37559 );
37560
37561 panel.addxtype({
37562         xtype : 'NestedLayoutPanel',
37563         region: 'west',
37564         layout: {
37565            center: { },
37566            west: { }   
37567         },
37568         items : [ ... list of content panels or nested layout panels.. ]
37569    }
37570 );
37571 </code></pre>
37572      * @param {Object} cfg Xtype definition of item to add.
37573      */
37574     addxtype : function(cfg) {
37575         return this.layout.addxtype(cfg);
37576     
37577     }
37578 });        /*
37579  * Based on:
37580  * Ext JS Library 1.1.1
37581  * Copyright(c) 2006-2007, Ext JS, LLC.
37582  *
37583  * Originally Released Under LGPL - original licence link has changed is not relivant.
37584  *
37585  * Fork - LGPL
37586  * <script type="text/javascript">
37587  */
37588 /**
37589  * @class Roo.TabPanel
37590  * @extends Roo.util.Observable
37591  * A lightweight tab container.
37592  * <br><br>
37593  * Usage:
37594  * <pre><code>
37595 // basic tabs 1, built from existing content
37596 var tabs = new Roo.TabPanel("tabs1");
37597 tabs.addTab("script", "View Script");
37598 tabs.addTab("markup", "View Markup");
37599 tabs.activate("script");
37600
37601 // more advanced tabs, built from javascript
37602 var jtabs = new Roo.TabPanel("jtabs");
37603 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37604
37605 // set up the UpdateManager
37606 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37607 var updater = tab2.getUpdateManager();
37608 updater.setDefaultUrl("ajax1.htm");
37609 tab2.on('activate', updater.refresh, updater, true);
37610
37611 // Use setUrl for Ajax loading
37612 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37613 tab3.setUrl("ajax2.htm", null, true);
37614
37615 // Disabled tab
37616 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37617 tab4.disable();
37618
37619 jtabs.activate("jtabs-1");
37620  * </code></pre>
37621  * @constructor
37622  * Create a new TabPanel.
37623  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37624  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37625  */
37626 Roo.bootstrap.panel.Tabs = function(config){
37627     /**
37628     * The container element for this TabPanel.
37629     * @type Roo.Element
37630     */
37631     this.el = Roo.get(config.el);
37632     delete config.el;
37633     if(config){
37634         if(typeof config == "boolean"){
37635             this.tabPosition = config ? "bottom" : "top";
37636         }else{
37637             Roo.apply(this, config);
37638         }
37639     }
37640     
37641     if(this.tabPosition == "bottom"){
37642         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37643         this.el.addClass("roo-tabs-bottom");
37644     }
37645     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37646     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37647     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37648     if(Roo.isIE){
37649         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37650     }
37651     if(this.tabPosition != "bottom"){
37652         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37653          * @type Roo.Element
37654          */
37655         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37656         this.el.addClass("roo-tabs-top");
37657     }
37658     this.items = [];
37659
37660     this.bodyEl.setStyle("position", "relative");
37661
37662     this.active = null;
37663     this.activateDelegate = this.activate.createDelegate(this);
37664
37665     this.addEvents({
37666         /**
37667          * @event tabchange
37668          * Fires when the active tab changes
37669          * @param {Roo.TabPanel} this
37670          * @param {Roo.TabPanelItem} activePanel The new active tab
37671          */
37672         "tabchange": true,
37673         /**
37674          * @event beforetabchange
37675          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37676          * @param {Roo.TabPanel} this
37677          * @param {Object} e Set cancel to true on this object to cancel the tab change
37678          * @param {Roo.TabPanelItem} tab The tab being changed to
37679          */
37680         "beforetabchange" : true
37681     });
37682
37683     Roo.EventManager.onWindowResize(this.onResize, this);
37684     this.cpad = this.el.getPadding("lr");
37685     this.hiddenCount = 0;
37686
37687
37688     // toolbar on the tabbar support...
37689     if (this.toolbar) {
37690         alert("no toolbar support yet");
37691         this.toolbar  = false;
37692         /*
37693         var tcfg = this.toolbar;
37694         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37695         this.toolbar = new Roo.Toolbar(tcfg);
37696         if (Roo.isSafari) {
37697             var tbl = tcfg.container.child('table', true);
37698             tbl.setAttribute('width', '100%');
37699         }
37700         */
37701         
37702     }
37703    
37704
37705
37706     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37707 };
37708
37709 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37710     /*
37711      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37712      */
37713     tabPosition : "top",
37714     /*
37715      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37716      */
37717     currentTabWidth : 0,
37718     /*
37719      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37720      */
37721     minTabWidth : 40,
37722     /*
37723      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37724      */
37725     maxTabWidth : 250,
37726     /*
37727      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37728      */
37729     preferredTabWidth : 175,
37730     /*
37731      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37732      */
37733     resizeTabs : false,
37734     /*
37735      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37736      */
37737     monitorResize : true,
37738     /*
37739      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37740      */
37741     toolbar : false,
37742
37743     /**
37744      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37745      * @param {String} id The id of the div to use <b>or create</b>
37746      * @param {String} text The text for the tab
37747      * @param {String} content (optional) Content to put in the TabPanelItem body
37748      * @param {Boolean} closable (optional) True to create a close icon on the tab
37749      * @return {Roo.TabPanelItem} The created TabPanelItem
37750      */
37751     addTab : function(id, text, content, closable, tpl)
37752     {
37753         var item = new Roo.bootstrap.panel.TabItem({
37754             panel: this,
37755             id : id,
37756             text : text,
37757             closable : closable,
37758             tpl : tpl
37759         });
37760         this.addTabItem(item);
37761         if(content){
37762             item.setContent(content);
37763         }
37764         return item;
37765     },
37766
37767     /**
37768      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37769      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37770      * @return {Roo.TabPanelItem}
37771      */
37772     getTab : function(id){
37773         return this.items[id];
37774     },
37775
37776     /**
37777      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37778      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37779      */
37780     hideTab : function(id){
37781         var t = this.items[id];
37782         if(!t.isHidden()){
37783            t.setHidden(true);
37784            this.hiddenCount++;
37785            this.autoSizeTabs();
37786         }
37787     },
37788
37789     /**
37790      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37791      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37792      */
37793     unhideTab : function(id){
37794         var t = this.items[id];
37795         if(t.isHidden()){
37796            t.setHidden(false);
37797            this.hiddenCount--;
37798            this.autoSizeTabs();
37799         }
37800     },
37801
37802     /**
37803      * Adds an existing {@link Roo.TabPanelItem}.
37804      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37805      */
37806     addTabItem : function(item){
37807         this.items[item.id] = item;
37808         this.items.push(item);
37809       //  if(this.resizeTabs){
37810     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37811   //         this.autoSizeTabs();
37812 //        }else{
37813 //            item.autoSize();
37814        // }
37815     },
37816
37817     /**
37818      * Removes a {@link Roo.TabPanelItem}.
37819      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37820      */
37821     removeTab : function(id){
37822         var items = this.items;
37823         var tab = items[id];
37824         if(!tab) { return; }
37825         var index = items.indexOf(tab);
37826         if(this.active == tab && items.length > 1){
37827             var newTab = this.getNextAvailable(index);
37828             if(newTab) {
37829                 newTab.activate();
37830             }
37831         }
37832         this.stripEl.dom.removeChild(tab.pnode.dom);
37833         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37834             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37835         }
37836         items.splice(index, 1);
37837         delete this.items[tab.id];
37838         tab.fireEvent("close", tab);
37839         tab.purgeListeners();
37840         this.autoSizeTabs();
37841     },
37842
37843     getNextAvailable : function(start){
37844         var items = this.items;
37845         var index = start;
37846         // look for a next tab that will slide over to
37847         // replace the one being removed
37848         while(index < items.length){
37849             var item = items[++index];
37850             if(item && !item.isHidden()){
37851                 return item;
37852             }
37853         }
37854         // if one isn't found select the previous tab (on the left)
37855         index = start;
37856         while(index >= 0){
37857             var item = items[--index];
37858             if(item && !item.isHidden()){
37859                 return item;
37860             }
37861         }
37862         return null;
37863     },
37864
37865     /**
37866      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37867      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37868      */
37869     disableTab : function(id){
37870         var tab = this.items[id];
37871         if(tab && this.active != tab){
37872             tab.disable();
37873         }
37874     },
37875
37876     /**
37877      * Enables a {@link Roo.TabPanelItem} that is disabled.
37878      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37879      */
37880     enableTab : function(id){
37881         var tab = this.items[id];
37882         tab.enable();
37883     },
37884
37885     /**
37886      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37887      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37888      * @return {Roo.TabPanelItem} The TabPanelItem.
37889      */
37890     activate : function(id){
37891         var tab = this.items[id];
37892         if(!tab){
37893             return null;
37894         }
37895         if(tab == this.active || tab.disabled){
37896             return tab;
37897         }
37898         var e = {};
37899         this.fireEvent("beforetabchange", this, e, tab);
37900         if(e.cancel !== true && !tab.disabled){
37901             if(this.active){
37902                 this.active.hide();
37903             }
37904             this.active = this.items[id];
37905             this.active.show();
37906             this.fireEvent("tabchange", this, this.active);
37907         }
37908         return tab;
37909     },
37910
37911     /**
37912      * Gets the active {@link Roo.TabPanelItem}.
37913      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37914      */
37915     getActiveTab : function(){
37916         return this.active;
37917     },
37918
37919     /**
37920      * Updates the tab body element to fit the height of the container element
37921      * for overflow scrolling
37922      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37923      */
37924     syncHeight : function(targetHeight){
37925         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37926         var bm = this.bodyEl.getMargins();
37927         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37928         this.bodyEl.setHeight(newHeight);
37929         return newHeight;
37930     },
37931
37932     onResize : function(){
37933         if(this.monitorResize){
37934             this.autoSizeTabs();
37935         }
37936     },
37937
37938     /**
37939      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37940      */
37941     beginUpdate : function(){
37942         this.updating = true;
37943     },
37944
37945     /**
37946      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37947      */
37948     endUpdate : function(){
37949         this.updating = false;
37950         this.autoSizeTabs();
37951     },
37952
37953     /**
37954      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37955      */
37956     autoSizeTabs : function(){
37957         var count = this.items.length;
37958         var vcount = count - this.hiddenCount;
37959         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37960             return;
37961         }
37962         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37963         var availWidth = Math.floor(w / vcount);
37964         var b = this.stripBody;
37965         if(b.getWidth() > w){
37966             var tabs = this.items;
37967             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37968             if(availWidth < this.minTabWidth){
37969                 /*if(!this.sleft){    // incomplete scrolling code
37970                     this.createScrollButtons();
37971                 }
37972                 this.showScroll();
37973                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37974             }
37975         }else{
37976             if(this.currentTabWidth < this.preferredTabWidth){
37977                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37978             }
37979         }
37980     },
37981
37982     /**
37983      * Returns the number of tabs in this TabPanel.
37984      * @return {Number}
37985      */
37986      getCount : function(){
37987          return this.items.length;
37988      },
37989
37990     /**
37991      * Resizes all the tabs to the passed width
37992      * @param {Number} The new width
37993      */
37994     setTabWidth : function(width){
37995         this.currentTabWidth = width;
37996         for(var i = 0, len = this.items.length; i < len; i++) {
37997                 if(!this.items[i].isHidden()) {
37998                 this.items[i].setWidth(width);
37999             }
38000         }
38001     },
38002
38003     /**
38004      * Destroys this TabPanel
38005      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38006      */
38007     destroy : function(removeEl){
38008         Roo.EventManager.removeResizeListener(this.onResize, this);
38009         for(var i = 0, len = this.items.length; i < len; i++){
38010             this.items[i].purgeListeners();
38011         }
38012         if(removeEl === true){
38013             this.el.update("");
38014             this.el.remove();
38015         }
38016     },
38017     
38018     createStrip : function(container)
38019     {
38020         var strip = document.createElement("nav");
38021         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38022         container.appendChild(strip);
38023         return strip;
38024     },
38025     
38026     createStripList : function(strip)
38027     {
38028         // div wrapper for retard IE
38029         // returns the "tr" element.
38030         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38031         //'<div class="x-tabs-strip-wrap">'+
38032           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38033           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38034         return strip.firstChild; //.firstChild.firstChild.firstChild;
38035     },
38036     createBody : function(container)
38037     {
38038         var body = document.createElement("div");
38039         Roo.id(body, "tab-body");
38040         //Roo.fly(body).addClass("x-tabs-body");
38041         Roo.fly(body).addClass("tab-content");
38042         container.appendChild(body);
38043         return body;
38044     },
38045     createItemBody :function(bodyEl, id){
38046         var body = Roo.getDom(id);
38047         if(!body){
38048             body = document.createElement("div");
38049             body.id = id;
38050         }
38051         //Roo.fly(body).addClass("x-tabs-item-body");
38052         Roo.fly(body).addClass("tab-pane");
38053          bodyEl.insertBefore(body, bodyEl.firstChild);
38054         return body;
38055     },
38056     /** @private */
38057     createStripElements :  function(stripEl, text, closable, tpl)
38058     {
38059         var td = document.createElement("li"); // was td..
38060         
38061         
38062         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38063         
38064         
38065         stripEl.appendChild(td);
38066         /*if(closable){
38067             td.className = "x-tabs-closable";
38068             if(!this.closeTpl){
38069                 this.closeTpl = new Roo.Template(
38070                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38071                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38072                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38073                 );
38074             }
38075             var el = this.closeTpl.overwrite(td, {"text": text});
38076             var close = el.getElementsByTagName("div")[0];
38077             var inner = el.getElementsByTagName("em")[0];
38078             return {"el": el, "close": close, "inner": inner};
38079         } else {
38080         */
38081         // not sure what this is..
38082 //            if(!this.tabTpl){
38083                 //this.tabTpl = new Roo.Template(
38084                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38085                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38086                 //);
38087 //                this.tabTpl = new Roo.Template(
38088 //                   '<a href="#">' +
38089 //                   '<span unselectable="on"' +
38090 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38091 //                            ' >{text}</span></a>'
38092 //                );
38093 //                
38094 //            }
38095
38096
38097             var template = tpl || this.tabTpl || false;
38098             
38099             if(!template){
38100                 
38101                 template = new Roo.Template(
38102                    '<a href="#">' +
38103                    '<span unselectable="on"' +
38104                             (this.disableTooltips ? '' : ' title="{text}"') +
38105                             ' >{text}</span></a>'
38106                 );
38107             }
38108             
38109             switch (typeof(template)) {
38110                 case 'object' :
38111                     break;
38112                 case 'string' :
38113                     template = new Roo.Template(template);
38114                     break;
38115                 default :
38116                     break;
38117             }
38118             
38119             var el = template.overwrite(td, {"text": text});
38120             
38121             var inner = el.getElementsByTagName("span")[0];
38122             
38123             return {"el": el, "inner": inner};
38124             
38125     }
38126         
38127     
38128 });
38129
38130 /**
38131  * @class Roo.TabPanelItem
38132  * @extends Roo.util.Observable
38133  * Represents an individual item (tab plus body) in a TabPanel.
38134  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38135  * @param {String} id The id of this TabPanelItem
38136  * @param {String} text The text for the tab of this TabPanelItem
38137  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38138  */
38139 Roo.bootstrap.panel.TabItem = function(config){
38140     /**
38141      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38142      * @type Roo.TabPanel
38143      */
38144     this.tabPanel = config.panel;
38145     /**
38146      * The id for this TabPanelItem
38147      * @type String
38148      */
38149     this.id = config.id;
38150     /** @private */
38151     this.disabled = false;
38152     /** @private */
38153     this.text = config.text;
38154     /** @private */
38155     this.loaded = false;
38156     this.closable = config.closable;
38157
38158     /**
38159      * The body element for this TabPanelItem.
38160      * @type Roo.Element
38161      */
38162     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38163     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38164     this.bodyEl.setStyle("display", "block");
38165     this.bodyEl.setStyle("zoom", "1");
38166     //this.hideAction();
38167
38168     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38169     /** @private */
38170     this.el = Roo.get(els.el);
38171     this.inner = Roo.get(els.inner, true);
38172     this.textEl = Roo.get(this.el.dom.firstChild, true);
38173     this.pnode = Roo.get(els.el.parentNode, true);
38174 //    this.el.on("mousedown", this.onTabMouseDown, this);
38175     this.el.on("click", this.onTabClick, this);
38176     /** @private */
38177     if(config.closable){
38178         var c = Roo.get(els.close, true);
38179         c.dom.title = this.closeText;
38180         c.addClassOnOver("close-over");
38181         c.on("click", this.closeClick, this);
38182      }
38183
38184     this.addEvents({
38185          /**
38186          * @event activate
38187          * Fires when this tab becomes the active tab.
38188          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38189          * @param {Roo.TabPanelItem} this
38190          */
38191         "activate": true,
38192         /**
38193          * @event beforeclose
38194          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38195          * @param {Roo.TabPanelItem} this
38196          * @param {Object} e Set cancel to true on this object to cancel the close.
38197          */
38198         "beforeclose": true,
38199         /**
38200          * @event close
38201          * Fires when this tab is closed.
38202          * @param {Roo.TabPanelItem} this
38203          */
38204          "close": true,
38205         /**
38206          * @event deactivate
38207          * Fires when this tab is no longer the active tab.
38208          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38209          * @param {Roo.TabPanelItem} this
38210          */
38211          "deactivate" : true
38212     });
38213     this.hidden = false;
38214
38215     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38216 };
38217
38218 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38219            {
38220     purgeListeners : function(){
38221        Roo.util.Observable.prototype.purgeListeners.call(this);
38222        this.el.removeAllListeners();
38223     },
38224     /**
38225      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38226      */
38227     show : function(){
38228         this.pnode.addClass("active");
38229         this.showAction();
38230         if(Roo.isOpera){
38231             this.tabPanel.stripWrap.repaint();
38232         }
38233         this.fireEvent("activate", this.tabPanel, this);
38234     },
38235
38236     /**
38237      * Returns true if this tab is the active tab.
38238      * @return {Boolean}
38239      */
38240     isActive : function(){
38241         return this.tabPanel.getActiveTab() == this;
38242     },
38243
38244     /**
38245      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38246      */
38247     hide : function(){
38248         this.pnode.removeClass("active");
38249         this.hideAction();
38250         this.fireEvent("deactivate", this.tabPanel, this);
38251     },
38252
38253     hideAction : function(){
38254         this.bodyEl.hide();
38255         this.bodyEl.setStyle("position", "absolute");
38256         this.bodyEl.setLeft("-20000px");
38257         this.bodyEl.setTop("-20000px");
38258     },
38259
38260     showAction : function(){
38261         this.bodyEl.setStyle("position", "relative");
38262         this.bodyEl.setTop("");
38263         this.bodyEl.setLeft("");
38264         this.bodyEl.show();
38265     },
38266
38267     /**
38268      * Set the tooltip for the tab.
38269      * @param {String} tooltip The tab's tooltip
38270      */
38271     setTooltip : function(text){
38272         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38273             this.textEl.dom.qtip = text;
38274             this.textEl.dom.removeAttribute('title');
38275         }else{
38276             this.textEl.dom.title = text;
38277         }
38278     },
38279
38280     onTabClick : function(e){
38281         e.preventDefault();
38282         this.tabPanel.activate(this.id);
38283     },
38284
38285     onTabMouseDown : function(e){
38286         e.preventDefault();
38287         this.tabPanel.activate(this.id);
38288     },
38289 /*
38290     getWidth : function(){
38291         return this.inner.getWidth();
38292     },
38293
38294     setWidth : function(width){
38295         var iwidth = width - this.pnode.getPadding("lr");
38296         this.inner.setWidth(iwidth);
38297         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38298         this.pnode.setWidth(width);
38299     },
38300 */
38301     /**
38302      * Show or hide the tab
38303      * @param {Boolean} hidden True to hide or false to show.
38304      */
38305     setHidden : function(hidden){
38306         this.hidden = hidden;
38307         this.pnode.setStyle("display", hidden ? "none" : "");
38308     },
38309
38310     /**
38311      * Returns true if this tab is "hidden"
38312      * @return {Boolean}
38313      */
38314     isHidden : function(){
38315         return this.hidden;
38316     },
38317
38318     /**
38319      * Returns the text for this tab
38320      * @return {String}
38321      */
38322     getText : function(){
38323         return this.text;
38324     },
38325     /*
38326     autoSize : function(){
38327         //this.el.beginMeasure();
38328         this.textEl.setWidth(1);
38329         /*
38330          *  #2804 [new] Tabs in Roojs
38331          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38332          */
38333         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38334         //this.el.endMeasure();
38335     //},
38336
38337     /**
38338      * Sets the text for the tab (Note: this also sets the tooltip text)
38339      * @param {String} text The tab's text and tooltip
38340      */
38341     setText : function(text){
38342         this.text = text;
38343         this.textEl.update(text);
38344         this.setTooltip(text);
38345         //if(!this.tabPanel.resizeTabs){
38346         //    this.autoSize();
38347         //}
38348     },
38349     /**
38350      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38351      */
38352     activate : function(){
38353         this.tabPanel.activate(this.id);
38354     },
38355
38356     /**
38357      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38358      */
38359     disable : function(){
38360         if(this.tabPanel.active != this){
38361             this.disabled = true;
38362             this.pnode.addClass("disabled");
38363         }
38364     },
38365
38366     /**
38367      * Enables this TabPanelItem if it was previously disabled.
38368      */
38369     enable : function(){
38370         this.disabled = false;
38371         this.pnode.removeClass("disabled");
38372     },
38373
38374     /**
38375      * Sets the content for this TabPanelItem.
38376      * @param {String} content The content
38377      * @param {Boolean} loadScripts true to look for and load scripts
38378      */
38379     setContent : function(content, loadScripts){
38380         this.bodyEl.update(content, loadScripts);
38381     },
38382
38383     /**
38384      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38385      * @return {Roo.UpdateManager} The UpdateManager
38386      */
38387     getUpdateManager : function(){
38388         return this.bodyEl.getUpdateManager();
38389     },
38390
38391     /**
38392      * Set a URL to be used to load the content for this TabPanelItem.
38393      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38394      * @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)
38395      * @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)
38396      * @return {Roo.UpdateManager} The UpdateManager
38397      */
38398     setUrl : function(url, params, loadOnce){
38399         if(this.refreshDelegate){
38400             this.un('activate', this.refreshDelegate);
38401         }
38402         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38403         this.on("activate", this.refreshDelegate);
38404         return this.bodyEl.getUpdateManager();
38405     },
38406
38407     /** @private */
38408     _handleRefresh : function(url, params, loadOnce){
38409         if(!loadOnce || !this.loaded){
38410             var updater = this.bodyEl.getUpdateManager();
38411             updater.update(url, params, this._setLoaded.createDelegate(this));
38412         }
38413     },
38414
38415     /**
38416      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38417      *   Will fail silently if the setUrl method has not been called.
38418      *   This does not activate the panel, just updates its content.
38419      */
38420     refresh : function(){
38421         if(this.refreshDelegate){
38422            this.loaded = false;
38423            this.refreshDelegate();
38424         }
38425     },
38426
38427     /** @private */
38428     _setLoaded : function(){
38429         this.loaded = true;
38430     },
38431
38432     /** @private */
38433     closeClick : function(e){
38434         var o = {};
38435         e.stopEvent();
38436         this.fireEvent("beforeclose", this, o);
38437         if(o.cancel !== true){
38438             this.tabPanel.removeTab(this.id);
38439         }
38440     },
38441     /**
38442      * The text displayed in the tooltip for the close icon.
38443      * @type String
38444      */
38445     closeText : "Close this tab"
38446 });
38447 /**
38448 *    This script refer to:
38449 *    Title: International Telephone Input
38450 *    Author: Jack O'Connor
38451 *    Code version:  v12.1.12
38452 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38453 **/
38454
38455 Roo.bootstrap.PhoneInputData = function() {
38456     var d = [
38457       [
38458         "Afghanistan (‫افغانستان‬‎)",
38459         "af",
38460         "93"
38461       ],
38462       [
38463         "Albania (Shqipëri)",
38464         "al",
38465         "355"
38466       ],
38467       [
38468         "Algeria (‫الجزائر‬‎)",
38469         "dz",
38470         "213"
38471       ],
38472       [
38473         "American Samoa",
38474         "as",
38475         "1684"
38476       ],
38477       [
38478         "Andorra",
38479         "ad",
38480         "376"
38481       ],
38482       [
38483         "Angola",
38484         "ao",
38485         "244"
38486       ],
38487       [
38488         "Anguilla",
38489         "ai",
38490         "1264"
38491       ],
38492       [
38493         "Antigua and Barbuda",
38494         "ag",
38495         "1268"
38496       ],
38497       [
38498         "Argentina",
38499         "ar",
38500         "54"
38501       ],
38502       [
38503         "Armenia (Հայաստան)",
38504         "am",
38505         "374"
38506       ],
38507       [
38508         "Aruba",
38509         "aw",
38510         "297"
38511       ],
38512       [
38513         "Australia",
38514         "au",
38515         "61",
38516         0
38517       ],
38518       [
38519         "Austria (Österreich)",
38520         "at",
38521         "43"
38522       ],
38523       [
38524         "Azerbaijan (Azərbaycan)",
38525         "az",
38526         "994"
38527       ],
38528       [
38529         "Bahamas",
38530         "bs",
38531         "1242"
38532       ],
38533       [
38534         "Bahrain (‫البحرين‬‎)",
38535         "bh",
38536         "973"
38537       ],
38538       [
38539         "Bangladesh (বাংলাদেশ)",
38540         "bd",
38541         "880"
38542       ],
38543       [
38544         "Barbados",
38545         "bb",
38546         "1246"
38547       ],
38548       [
38549         "Belarus (Беларусь)",
38550         "by",
38551         "375"
38552       ],
38553       [
38554         "Belgium (België)",
38555         "be",
38556         "32"
38557       ],
38558       [
38559         "Belize",
38560         "bz",
38561         "501"
38562       ],
38563       [
38564         "Benin (Bénin)",
38565         "bj",
38566         "229"
38567       ],
38568       [
38569         "Bermuda",
38570         "bm",
38571         "1441"
38572       ],
38573       [
38574         "Bhutan (འབྲུག)",
38575         "bt",
38576         "975"
38577       ],
38578       [
38579         "Bolivia",
38580         "bo",
38581         "591"
38582       ],
38583       [
38584         "Bosnia and Herzegovina (Босна и Херцеговина)",
38585         "ba",
38586         "387"
38587       ],
38588       [
38589         "Botswana",
38590         "bw",
38591         "267"
38592       ],
38593       [
38594         "Brazil (Brasil)",
38595         "br",
38596         "55"
38597       ],
38598       [
38599         "British Indian Ocean Territory",
38600         "io",
38601         "246"
38602       ],
38603       [
38604         "British Virgin Islands",
38605         "vg",
38606         "1284"
38607       ],
38608       [
38609         "Brunei",
38610         "bn",
38611         "673"
38612       ],
38613       [
38614         "Bulgaria (България)",
38615         "bg",
38616         "359"
38617       ],
38618       [
38619         "Burkina Faso",
38620         "bf",
38621         "226"
38622       ],
38623       [
38624         "Burundi (Uburundi)",
38625         "bi",
38626         "257"
38627       ],
38628       [
38629         "Cambodia (កម្ពុជា)",
38630         "kh",
38631         "855"
38632       ],
38633       [
38634         "Cameroon (Cameroun)",
38635         "cm",
38636         "237"
38637       ],
38638       [
38639         "Canada",
38640         "ca",
38641         "1",
38642         1,
38643         ["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"]
38644       ],
38645       [
38646         "Cape Verde (Kabu Verdi)",
38647         "cv",
38648         "238"
38649       ],
38650       [
38651         "Caribbean Netherlands",
38652         "bq",
38653         "599",
38654         1
38655       ],
38656       [
38657         "Cayman Islands",
38658         "ky",
38659         "1345"
38660       ],
38661       [
38662         "Central African Republic (République centrafricaine)",
38663         "cf",
38664         "236"
38665       ],
38666       [
38667         "Chad (Tchad)",
38668         "td",
38669         "235"
38670       ],
38671       [
38672         "Chile",
38673         "cl",
38674         "56"
38675       ],
38676       [
38677         "China (中国)",
38678         "cn",
38679         "86"
38680       ],
38681       [
38682         "Christmas Island",
38683         "cx",
38684         "61",
38685         2
38686       ],
38687       [
38688         "Cocos (Keeling) Islands",
38689         "cc",
38690         "61",
38691         1
38692       ],
38693       [
38694         "Colombia",
38695         "co",
38696         "57"
38697       ],
38698       [
38699         "Comoros (‫جزر القمر‬‎)",
38700         "km",
38701         "269"
38702       ],
38703       [
38704         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38705         "cd",
38706         "243"
38707       ],
38708       [
38709         "Congo (Republic) (Congo-Brazzaville)",
38710         "cg",
38711         "242"
38712       ],
38713       [
38714         "Cook Islands",
38715         "ck",
38716         "682"
38717       ],
38718       [
38719         "Costa Rica",
38720         "cr",
38721         "506"
38722       ],
38723       [
38724         "Côte d’Ivoire",
38725         "ci",
38726         "225"
38727       ],
38728       [
38729         "Croatia (Hrvatska)",
38730         "hr",
38731         "385"
38732       ],
38733       [
38734         "Cuba",
38735         "cu",
38736         "53"
38737       ],
38738       [
38739         "Curaçao",
38740         "cw",
38741         "599",
38742         0
38743       ],
38744       [
38745         "Cyprus (Κύπρος)",
38746         "cy",
38747         "357"
38748       ],
38749       [
38750         "Czech Republic (Česká republika)",
38751         "cz",
38752         "420"
38753       ],
38754       [
38755         "Denmark (Danmark)",
38756         "dk",
38757         "45"
38758       ],
38759       [
38760         "Djibouti",
38761         "dj",
38762         "253"
38763       ],
38764       [
38765         "Dominica",
38766         "dm",
38767         "1767"
38768       ],
38769       [
38770         "Dominican Republic (República Dominicana)",
38771         "do",
38772         "1",
38773         2,
38774         ["809", "829", "849"]
38775       ],
38776       [
38777         "Ecuador",
38778         "ec",
38779         "593"
38780       ],
38781       [
38782         "Egypt (‫مصر‬‎)",
38783         "eg",
38784         "20"
38785       ],
38786       [
38787         "El Salvador",
38788         "sv",
38789         "503"
38790       ],
38791       [
38792         "Equatorial Guinea (Guinea Ecuatorial)",
38793         "gq",
38794         "240"
38795       ],
38796       [
38797         "Eritrea",
38798         "er",
38799         "291"
38800       ],
38801       [
38802         "Estonia (Eesti)",
38803         "ee",
38804         "372"
38805       ],
38806       [
38807         "Ethiopia",
38808         "et",
38809         "251"
38810       ],
38811       [
38812         "Falkland Islands (Islas Malvinas)",
38813         "fk",
38814         "500"
38815       ],
38816       [
38817         "Faroe Islands (Føroyar)",
38818         "fo",
38819         "298"
38820       ],
38821       [
38822         "Fiji",
38823         "fj",
38824         "679"
38825       ],
38826       [
38827         "Finland (Suomi)",
38828         "fi",
38829         "358",
38830         0
38831       ],
38832       [
38833         "France",
38834         "fr",
38835         "33"
38836       ],
38837       [
38838         "French Guiana (Guyane française)",
38839         "gf",
38840         "594"
38841       ],
38842       [
38843         "French Polynesia (Polynésie française)",
38844         "pf",
38845         "689"
38846       ],
38847       [
38848         "Gabon",
38849         "ga",
38850         "241"
38851       ],
38852       [
38853         "Gambia",
38854         "gm",
38855         "220"
38856       ],
38857       [
38858         "Georgia (საქართველო)",
38859         "ge",
38860         "995"
38861       ],
38862       [
38863         "Germany (Deutschland)",
38864         "de",
38865         "49"
38866       ],
38867       [
38868         "Ghana (Gaana)",
38869         "gh",
38870         "233"
38871       ],
38872       [
38873         "Gibraltar",
38874         "gi",
38875         "350"
38876       ],
38877       [
38878         "Greece (Ελλάδα)",
38879         "gr",
38880         "30"
38881       ],
38882       [
38883         "Greenland (Kalaallit Nunaat)",
38884         "gl",
38885         "299"
38886       ],
38887       [
38888         "Grenada",
38889         "gd",
38890         "1473"
38891       ],
38892       [
38893         "Guadeloupe",
38894         "gp",
38895         "590",
38896         0
38897       ],
38898       [
38899         "Guam",
38900         "gu",
38901         "1671"
38902       ],
38903       [
38904         "Guatemala",
38905         "gt",
38906         "502"
38907       ],
38908       [
38909         "Guernsey",
38910         "gg",
38911         "44",
38912         1
38913       ],
38914       [
38915         "Guinea (Guinée)",
38916         "gn",
38917         "224"
38918       ],
38919       [
38920         "Guinea-Bissau (Guiné Bissau)",
38921         "gw",
38922         "245"
38923       ],
38924       [
38925         "Guyana",
38926         "gy",
38927         "592"
38928       ],
38929       [
38930         "Haiti",
38931         "ht",
38932         "509"
38933       ],
38934       [
38935         "Honduras",
38936         "hn",
38937         "504"
38938       ],
38939       [
38940         "Hong Kong (香港)",
38941         "hk",
38942         "852"
38943       ],
38944       [
38945         "Hungary (Magyarország)",
38946         "hu",
38947         "36"
38948       ],
38949       [
38950         "Iceland (Ísland)",
38951         "is",
38952         "354"
38953       ],
38954       [
38955         "India (भारत)",
38956         "in",
38957         "91"
38958       ],
38959       [
38960         "Indonesia",
38961         "id",
38962         "62"
38963       ],
38964       [
38965         "Iran (‫ایران‬‎)",
38966         "ir",
38967         "98"
38968       ],
38969       [
38970         "Iraq (‫العراق‬‎)",
38971         "iq",
38972         "964"
38973       ],
38974       [
38975         "Ireland",
38976         "ie",
38977         "353"
38978       ],
38979       [
38980         "Isle of Man",
38981         "im",
38982         "44",
38983         2
38984       ],
38985       [
38986         "Israel (‫ישראל‬‎)",
38987         "il",
38988         "972"
38989       ],
38990       [
38991         "Italy (Italia)",
38992         "it",
38993         "39",
38994         0
38995       ],
38996       [
38997         "Jamaica",
38998         "jm",
38999         "1876"
39000       ],
39001       [
39002         "Japan (日本)",
39003         "jp",
39004         "81"
39005       ],
39006       [
39007         "Jersey",
39008         "je",
39009         "44",
39010         3
39011       ],
39012       [
39013         "Jordan (‫الأردن‬‎)",
39014         "jo",
39015         "962"
39016       ],
39017       [
39018         "Kazakhstan (Казахстан)",
39019         "kz",
39020         "7",
39021         1
39022       ],
39023       [
39024         "Kenya",
39025         "ke",
39026         "254"
39027       ],
39028       [
39029         "Kiribati",
39030         "ki",
39031         "686"
39032       ],
39033       [
39034         "Kosovo",
39035         "xk",
39036         "383"
39037       ],
39038       [
39039         "Kuwait (‫الكويت‬‎)",
39040         "kw",
39041         "965"
39042       ],
39043       [
39044         "Kyrgyzstan (Кыргызстан)",
39045         "kg",
39046         "996"
39047       ],
39048       [
39049         "Laos (ລາວ)",
39050         "la",
39051         "856"
39052       ],
39053       [
39054         "Latvia (Latvija)",
39055         "lv",
39056         "371"
39057       ],
39058       [
39059         "Lebanon (‫لبنان‬‎)",
39060         "lb",
39061         "961"
39062       ],
39063       [
39064         "Lesotho",
39065         "ls",
39066         "266"
39067       ],
39068       [
39069         "Liberia",
39070         "lr",
39071         "231"
39072       ],
39073       [
39074         "Libya (‫ليبيا‬‎)",
39075         "ly",
39076         "218"
39077       ],
39078       [
39079         "Liechtenstein",
39080         "li",
39081         "423"
39082       ],
39083       [
39084         "Lithuania (Lietuva)",
39085         "lt",
39086         "370"
39087       ],
39088       [
39089         "Luxembourg",
39090         "lu",
39091         "352"
39092       ],
39093       [
39094         "Macau (澳門)",
39095         "mo",
39096         "853"
39097       ],
39098       [
39099         "Macedonia (FYROM) (Македонија)",
39100         "mk",
39101         "389"
39102       ],
39103       [
39104         "Madagascar (Madagasikara)",
39105         "mg",
39106         "261"
39107       ],
39108       [
39109         "Malawi",
39110         "mw",
39111         "265"
39112       ],
39113       [
39114         "Malaysia",
39115         "my",
39116         "60"
39117       ],
39118       [
39119         "Maldives",
39120         "mv",
39121         "960"
39122       ],
39123       [
39124         "Mali",
39125         "ml",
39126         "223"
39127       ],
39128       [
39129         "Malta",
39130         "mt",
39131         "356"
39132       ],
39133       [
39134         "Marshall Islands",
39135         "mh",
39136         "692"
39137       ],
39138       [
39139         "Martinique",
39140         "mq",
39141         "596"
39142       ],
39143       [
39144         "Mauritania (‫موريتانيا‬‎)",
39145         "mr",
39146         "222"
39147       ],
39148       [
39149         "Mauritius (Moris)",
39150         "mu",
39151         "230"
39152       ],
39153       [
39154         "Mayotte",
39155         "yt",
39156         "262",
39157         1
39158       ],
39159       [
39160         "Mexico (México)",
39161         "mx",
39162         "52"
39163       ],
39164       [
39165         "Micronesia",
39166         "fm",
39167         "691"
39168       ],
39169       [
39170         "Moldova (Republica Moldova)",
39171         "md",
39172         "373"
39173       ],
39174       [
39175         "Monaco",
39176         "mc",
39177         "377"
39178       ],
39179       [
39180         "Mongolia (Монгол)",
39181         "mn",
39182         "976"
39183       ],
39184       [
39185         "Montenegro (Crna Gora)",
39186         "me",
39187         "382"
39188       ],
39189       [
39190         "Montserrat",
39191         "ms",
39192         "1664"
39193       ],
39194       [
39195         "Morocco (‫المغرب‬‎)",
39196         "ma",
39197         "212",
39198         0
39199       ],
39200       [
39201         "Mozambique (Moçambique)",
39202         "mz",
39203         "258"
39204       ],
39205       [
39206         "Myanmar (Burma) (မြန်မာ)",
39207         "mm",
39208         "95"
39209       ],
39210       [
39211         "Namibia (Namibië)",
39212         "na",
39213         "264"
39214       ],
39215       [
39216         "Nauru",
39217         "nr",
39218         "674"
39219       ],
39220       [
39221         "Nepal (नेपाल)",
39222         "np",
39223         "977"
39224       ],
39225       [
39226         "Netherlands (Nederland)",
39227         "nl",
39228         "31"
39229       ],
39230       [
39231         "New Caledonia (Nouvelle-Calédonie)",
39232         "nc",
39233         "687"
39234       ],
39235       [
39236         "New Zealand",
39237         "nz",
39238         "64"
39239       ],
39240       [
39241         "Nicaragua",
39242         "ni",
39243         "505"
39244       ],
39245       [
39246         "Niger (Nijar)",
39247         "ne",
39248         "227"
39249       ],
39250       [
39251         "Nigeria",
39252         "ng",
39253         "234"
39254       ],
39255       [
39256         "Niue",
39257         "nu",
39258         "683"
39259       ],
39260       [
39261         "Norfolk Island",
39262         "nf",
39263         "672"
39264       ],
39265       [
39266         "North Korea (조선 민주주의 인민 공화국)",
39267         "kp",
39268         "850"
39269       ],
39270       [
39271         "Northern Mariana Islands",
39272         "mp",
39273         "1670"
39274       ],
39275       [
39276         "Norway (Norge)",
39277         "no",
39278         "47",
39279         0
39280       ],
39281       [
39282         "Oman (‫عُمان‬‎)",
39283         "om",
39284         "968"
39285       ],
39286       [
39287         "Pakistan (‫پاکستان‬‎)",
39288         "pk",
39289         "92"
39290       ],
39291       [
39292         "Palau",
39293         "pw",
39294         "680"
39295       ],
39296       [
39297         "Palestine (‫فلسطين‬‎)",
39298         "ps",
39299         "970"
39300       ],
39301       [
39302         "Panama (Panamá)",
39303         "pa",
39304         "507"
39305       ],
39306       [
39307         "Papua New Guinea",
39308         "pg",
39309         "675"
39310       ],
39311       [
39312         "Paraguay",
39313         "py",
39314         "595"
39315       ],
39316       [
39317         "Peru (Perú)",
39318         "pe",
39319         "51"
39320       ],
39321       [
39322         "Philippines",
39323         "ph",
39324         "63"
39325       ],
39326       [
39327         "Poland (Polska)",
39328         "pl",
39329         "48"
39330       ],
39331       [
39332         "Portugal",
39333         "pt",
39334         "351"
39335       ],
39336       [
39337         "Puerto Rico",
39338         "pr",
39339         "1",
39340         3,
39341         ["787", "939"]
39342       ],
39343       [
39344         "Qatar (‫قطر‬‎)",
39345         "qa",
39346         "974"
39347       ],
39348       [
39349         "Réunion (La Réunion)",
39350         "re",
39351         "262",
39352         0
39353       ],
39354       [
39355         "Romania (România)",
39356         "ro",
39357         "40"
39358       ],
39359       [
39360         "Russia (Россия)",
39361         "ru",
39362         "7",
39363         0
39364       ],
39365       [
39366         "Rwanda",
39367         "rw",
39368         "250"
39369       ],
39370       [
39371         "Saint Barthélemy",
39372         "bl",
39373         "590",
39374         1
39375       ],
39376       [
39377         "Saint Helena",
39378         "sh",
39379         "290"
39380       ],
39381       [
39382         "Saint Kitts and Nevis",
39383         "kn",
39384         "1869"
39385       ],
39386       [
39387         "Saint Lucia",
39388         "lc",
39389         "1758"
39390       ],
39391       [
39392         "Saint Martin (Saint-Martin (partie française))",
39393         "mf",
39394         "590",
39395         2
39396       ],
39397       [
39398         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39399         "pm",
39400         "508"
39401       ],
39402       [
39403         "Saint Vincent and the Grenadines",
39404         "vc",
39405         "1784"
39406       ],
39407       [
39408         "Samoa",
39409         "ws",
39410         "685"
39411       ],
39412       [
39413         "San Marino",
39414         "sm",
39415         "378"
39416       ],
39417       [
39418         "São Tomé and Príncipe (São Tomé e Príncipe)",
39419         "st",
39420         "239"
39421       ],
39422       [
39423         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39424         "sa",
39425         "966"
39426       ],
39427       [
39428         "Senegal (Sénégal)",
39429         "sn",
39430         "221"
39431       ],
39432       [
39433         "Serbia (Србија)",
39434         "rs",
39435         "381"
39436       ],
39437       [
39438         "Seychelles",
39439         "sc",
39440         "248"
39441       ],
39442       [
39443         "Sierra Leone",
39444         "sl",
39445         "232"
39446       ],
39447       [
39448         "Singapore",
39449         "sg",
39450         "65"
39451       ],
39452       [
39453         "Sint Maarten",
39454         "sx",
39455         "1721"
39456       ],
39457       [
39458         "Slovakia (Slovensko)",
39459         "sk",
39460         "421"
39461       ],
39462       [
39463         "Slovenia (Slovenija)",
39464         "si",
39465         "386"
39466       ],
39467       [
39468         "Solomon Islands",
39469         "sb",
39470         "677"
39471       ],
39472       [
39473         "Somalia (Soomaaliya)",
39474         "so",
39475         "252"
39476       ],
39477       [
39478         "South Africa",
39479         "za",
39480         "27"
39481       ],
39482       [
39483         "South Korea (대한민국)",
39484         "kr",
39485         "82"
39486       ],
39487       [
39488         "South Sudan (‫جنوب السودان‬‎)",
39489         "ss",
39490         "211"
39491       ],
39492       [
39493         "Spain (España)",
39494         "es",
39495         "34"
39496       ],
39497       [
39498         "Sri Lanka (ශ්‍රී ලංකාව)",
39499         "lk",
39500         "94"
39501       ],
39502       [
39503         "Sudan (‫السودان‬‎)",
39504         "sd",
39505         "249"
39506       ],
39507       [
39508         "Suriname",
39509         "sr",
39510         "597"
39511       ],
39512       [
39513         "Svalbard and Jan Mayen",
39514         "sj",
39515         "47",
39516         1
39517       ],
39518       [
39519         "Swaziland",
39520         "sz",
39521         "268"
39522       ],
39523       [
39524         "Sweden (Sverige)",
39525         "se",
39526         "46"
39527       ],
39528       [
39529         "Switzerland (Schweiz)",
39530         "ch",
39531         "41"
39532       ],
39533       [
39534         "Syria (‫سوريا‬‎)",
39535         "sy",
39536         "963"
39537       ],
39538       [
39539         "Taiwan (台灣)",
39540         "tw",
39541         "886"
39542       ],
39543       [
39544         "Tajikistan",
39545         "tj",
39546         "992"
39547       ],
39548       [
39549         "Tanzania",
39550         "tz",
39551         "255"
39552       ],
39553       [
39554         "Thailand (ไทย)",
39555         "th",
39556         "66"
39557       ],
39558       [
39559         "Timor-Leste",
39560         "tl",
39561         "670"
39562       ],
39563       [
39564         "Togo",
39565         "tg",
39566         "228"
39567       ],
39568       [
39569         "Tokelau",
39570         "tk",
39571         "690"
39572       ],
39573       [
39574         "Tonga",
39575         "to",
39576         "676"
39577       ],
39578       [
39579         "Trinidad and Tobago",
39580         "tt",
39581         "1868"
39582       ],
39583       [
39584         "Tunisia (‫تونس‬‎)",
39585         "tn",
39586         "216"
39587       ],
39588       [
39589         "Turkey (Türkiye)",
39590         "tr",
39591         "90"
39592       ],
39593       [
39594         "Turkmenistan",
39595         "tm",
39596         "993"
39597       ],
39598       [
39599         "Turks and Caicos Islands",
39600         "tc",
39601         "1649"
39602       ],
39603       [
39604         "Tuvalu",
39605         "tv",
39606         "688"
39607       ],
39608       [
39609         "U.S. Virgin Islands",
39610         "vi",
39611         "1340"
39612       ],
39613       [
39614         "Uganda",
39615         "ug",
39616         "256"
39617       ],
39618       [
39619         "Ukraine (Україна)",
39620         "ua",
39621         "380"
39622       ],
39623       [
39624         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39625         "ae",
39626         "971"
39627       ],
39628       [
39629         "United Kingdom",
39630         "gb",
39631         "44",
39632         0
39633       ],
39634       [
39635         "United States",
39636         "us",
39637         "1",
39638         0
39639       ],
39640       [
39641         "Uruguay",
39642         "uy",
39643         "598"
39644       ],
39645       [
39646         "Uzbekistan (Oʻzbekiston)",
39647         "uz",
39648         "998"
39649       ],
39650       [
39651         "Vanuatu",
39652         "vu",
39653         "678"
39654       ],
39655       [
39656         "Vatican City (Città del Vaticano)",
39657         "va",
39658         "39",
39659         1
39660       ],
39661       [
39662         "Venezuela",
39663         "ve",
39664         "58"
39665       ],
39666       [
39667         "Vietnam (Việt Nam)",
39668         "vn",
39669         "84"
39670       ],
39671       [
39672         "Wallis and Futuna (Wallis-et-Futuna)",
39673         "wf",
39674         "681"
39675       ],
39676       [
39677         "Western Sahara (‫الصحراء الغربية‬‎)",
39678         "eh",
39679         "212",
39680         1
39681       ],
39682       [
39683         "Yemen (‫اليمن‬‎)",
39684         "ye",
39685         "967"
39686       ],
39687       [
39688         "Zambia",
39689         "zm",
39690         "260"
39691       ],
39692       [
39693         "Zimbabwe",
39694         "zw",
39695         "263"
39696       ],
39697       [
39698         "Åland Islands",
39699         "ax",
39700         "358",
39701         1
39702       ]
39703   ];
39704   
39705   return d;
39706 }/**
39707 *    This script refer to:
39708 *    Title: International Telephone Input
39709 *    Author: Jack O'Connor
39710 *    Code version:  v12.1.12
39711 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39712 **/
39713
39714 /**
39715  * @class Roo.bootstrap.PhoneInput
39716  * @extends Roo.bootstrap.TriggerField
39717  * An input with International dial-code selection
39718  
39719  * @cfg {String} defaultDialCode default '+852'
39720  * @cfg {Array} preferedCountries default []
39721   
39722  * @constructor
39723  * Create a new PhoneInput.
39724  * @param {Object} config Configuration options
39725  */
39726
39727 Roo.bootstrap.PhoneInput = function(config) {
39728     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39729 };
39730
39731 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39732         
39733         listWidth: undefined,
39734         
39735         selectedClass: 'active',
39736         
39737         invalidClass : "has-warning",
39738         
39739         validClass: 'has-success',
39740         
39741         allowed: '0123456789',
39742         
39743         /**
39744          * @cfg {String} defaultDialCode The default dial code when initializing the input
39745          */
39746         defaultDialCode: '+852',
39747         
39748         /**
39749          * @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
39750          */
39751         preferedCountries: false,
39752         
39753         getAutoCreate : function()
39754         {
39755             var data = Roo.bootstrap.PhoneInputData();
39756             var align = this.labelAlign || this.parentLabelAlign();
39757             var id = Roo.id();
39758             
39759             this.allCountries = [];
39760             this.dialCodeMapping = [];
39761             
39762             for (var i = 0; i < data.length; i++) {
39763               var c = data[i];
39764               this.allCountries[i] = {
39765                 name: c[0],
39766                 iso2: c[1],
39767                 dialCode: c[2],
39768                 priority: c[3] || 0,
39769                 areaCodes: c[4] || null
39770               };
39771               this.dialCodeMapping[c[2]] = {
39772                   name: c[0],
39773                   iso2: c[1],
39774                   priority: c[3] || 0,
39775                   areaCodes: c[4] || null
39776               };
39777             }
39778             
39779             var cfg = {
39780                 cls: 'form-group',
39781                 cn: []
39782             };
39783             
39784             var input =  {
39785                 tag: 'input',
39786                 id : id,
39787                 cls : 'form-control tel-input',
39788                 autocomplete: 'new-password'
39789             };
39790             
39791             var hiddenInput = {
39792                 tag: 'input',
39793                 type: 'hidden',
39794                 cls: 'hidden-tel-input'
39795             };
39796             
39797             if (this.name) {
39798                 hiddenInput.name = this.name;
39799             }
39800             
39801             if (this.disabled) {
39802                 input.disabled = true;
39803             }
39804             
39805             var flag_container = {
39806                 tag: 'div',
39807                 cls: 'flag-box',
39808                 cn: [
39809                     {
39810                         tag: 'div',
39811                         cls: 'flag'
39812                     },
39813                     {
39814                         tag: 'div',
39815                         cls: 'caret'
39816                     }
39817                 ]
39818             };
39819             
39820             var box = {
39821                 tag: 'div',
39822                 cls: this.hasFeedback ? 'has-feedback' : '',
39823                 cn: [
39824                     hiddenInput,
39825                     input,
39826                     {
39827                         tag: 'input',
39828                         cls: 'dial-code-holder',
39829                         disabled: true
39830                     }
39831                 ]
39832             };
39833             
39834             var container = {
39835                 cls: 'roo-select2-container input-group',
39836                 cn: [
39837                     flag_container,
39838                     box
39839                 ]
39840             };
39841             
39842             if (this.fieldLabel.length) {
39843                 var indicator = {
39844                     tag: 'i',
39845                     tooltip: 'This field is required'
39846                 };
39847                 
39848                 var label = {
39849                     tag: 'label',
39850                     'for':  id,
39851                     cls: 'control-label',
39852                     cn: []
39853                 };
39854                 
39855                 var label_text = {
39856                     tag: 'span',
39857                     html: this.fieldLabel
39858                 };
39859                 
39860                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39861                 label.cn = [
39862                     indicator,
39863                     label_text
39864                 ];
39865                 
39866                 if(this.indicatorpos == 'right') {
39867                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39868                     label.cn = [
39869                         label_text,
39870                         indicator
39871                     ];
39872                 }
39873                 
39874                 if(align == 'left') {
39875                     container = {
39876                         tag: 'div',
39877                         cn: [
39878                             container
39879                         ]
39880                     };
39881                     
39882                     if(this.labelWidth > 12){
39883                         label.style = "width: " + this.labelWidth + 'px';
39884                     }
39885                     if(this.labelWidth < 13 && this.labelmd == 0){
39886                         this.labelmd = this.labelWidth;
39887                     }
39888                     if(this.labellg > 0){
39889                         label.cls += ' col-lg-' + this.labellg;
39890                         input.cls += ' col-lg-' + (12 - this.labellg);
39891                     }
39892                     if(this.labelmd > 0){
39893                         label.cls += ' col-md-' + this.labelmd;
39894                         container.cls += ' col-md-' + (12 - this.labelmd);
39895                     }
39896                     if(this.labelsm > 0){
39897                         label.cls += ' col-sm-' + this.labelsm;
39898                         container.cls += ' col-sm-' + (12 - this.labelsm);
39899                     }
39900                     if(this.labelxs > 0){
39901                         label.cls += ' col-xs-' + this.labelxs;
39902                         container.cls += ' col-xs-' + (12 - this.labelxs);
39903                     }
39904                 }
39905             }
39906             
39907             cfg.cn = [
39908                 label,
39909                 container
39910             ];
39911             
39912             var settings = this;
39913             
39914             ['xs','sm','md','lg'].map(function(size){
39915                 if (settings[size]) {
39916                     cfg.cls += ' col-' + size + '-' + settings[size];
39917                 }
39918             });
39919             
39920             this.store = new Roo.data.Store({
39921                 proxy : new Roo.data.MemoryProxy({}),
39922                 reader : new Roo.data.JsonReader({
39923                     fields : [
39924                         {
39925                             'name' : 'name',
39926                             'type' : 'string'
39927                         },
39928                         {
39929                             'name' : 'iso2',
39930                             'type' : 'string'
39931                         },
39932                         {
39933                             'name' : 'dialCode',
39934                             'type' : 'string'
39935                         },
39936                         {
39937                             'name' : 'priority',
39938                             'type' : 'string'
39939                         },
39940                         {
39941                             'name' : 'areaCodes',
39942                             'type' : 'string'
39943                         }
39944                     ]
39945                 })
39946             });
39947             
39948             if(!this.preferedCountries) {
39949                 this.preferedCountries = [
39950                     'hk',
39951                     'gb',
39952                     'us'
39953                 ];
39954             }
39955             
39956             var p = this.preferedCountries.reverse();
39957             
39958             if(p) {
39959                 for (var i = 0; i < p.length; i++) {
39960                     for (var j = 0; j < this.allCountries.length; j++) {
39961                         if(this.allCountries[j].iso2 == p[i]) {
39962                             var t = this.allCountries[j];
39963                             this.allCountries.splice(j,1);
39964                             this.allCountries.unshift(t);
39965                         }
39966                     } 
39967                 }
39968             }
39969             
39970             this.store.proxy.data = {
39971                 success: true,
39972                 data: this.allCountries
39973             };
39974             
39975             return cfg;
39976         },
39977         
39978         initEvents : function()
39979         {
39980             this.createList();
39981             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39982             
39983             this.indicator = this.indicatorEl();
39984             this.flag = this.flagEl();
39985             this.dialCodeHolder = this.dialCodeHolderEl();
39986             
39987             this.trigger = this.el.select('div.flag-box',true).first();
39988             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39989             
39990             var _this = this;
39991             
39992             (function(){
39993                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39994                 _this.list.setWidth(lw);
39995             }).defer(100);
39996             
39997             this.list.on('mouseover', this.onViewOver, this);
39998             this.list.on('mousemove', this.onViewMove, this);
39999             this.inputEl().on("keyup", this.onKeyUp, this);
40000             
40001             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40002
40003             this.view = new Roo.View(this.list, this.tpl, {
40004                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40005             });
40006             
40007             this.view.on('click', this.onViewClick, this);
40008             this.setValue(this.defaultDialCode);
40009         },
40010         
40011         onTriggerClick : function(e)
40012         {
40013             Roo.log('trigger click');
40014             if(this.disabled){
40015                 return;
40016             }
40017             
40018             if(this.isExpanded()){
40019                 this.collapse();
40020                 this.hasFocus = false;
40021             }else {
40022                 this.store.load({});
40023                 this.hasFocus = true;
40024                 this.expand();
40025             }
40026         },
40027         
40028         isExpanded : function()
40029         {
40030             return this.list.isVisible();
40031         },
40032         
40033         collapse : function()
40034         {
40035             if(!this.isExpanded()){
40036                 return;
40037             }
40038             this.list.hide();
40039             Roo.get(document).un('mousedown', this.collapseIf, this);
40040             Roo.get(document).un('mousewheel', this.collapseIf, this);
40041             this.fireEvent('collapse', this);
40042             this.validate();
40043         },
40044         
40045         expand : function()
40046         {
40047             Roo.log('expand');
40048
40049             if(this.isExpanded() || !this.hasFocus){
40050                 return;
40051             }
40052             
40053             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40054             this.list.setWidth(lw);
40055             
40056             this.list.show();
40057             this.restrictHeight();
40058             
40059             Roo.get(document).on('mousedown', this.collapseIf, this);
40060             Roo.get(document).on('mousewheel', this.collapseIf, this);
40061             
40062             this.fireEvent('expand', this);
40063         },
40064         
40065         restrictHeight : function()
40066         {
40067             this.list.alignTo(this.inputEl(), this.listAlign);
40068             this.list.alignTo(this.inputEl(), this.listAlign);
40069         },
40070         
40071         onViewOver : function(e, t)
40072         {
40073             if(this.inKeyMode){
40074                 return;
40075             }
40076             var item = this.view.findItemFromChild(t);
40077             
40078             if(item){
40079                 var index = this.view.indexOf(item);
40080                 this.select(index, false);
40081             }
40082         },
40083
40084         // private
40085         onViewClick : function(view, doFocus, el, e)
40086         {
40087             var index = this.view.getSelectedIndexes()[0];
40088             
40089             var r = this.store.getAt(index);
40090             
40091             if(r){
40092                 this.onSelect(r, index);
40093             }
40094             if(doFocus !== false && !this.blockFocus){
40095                 this.inputEl().focus();
40096             }
40097         },
40098         
40099         onViewMove : function(e, t)
40100         {
40101             this.inKeyMode = false;
40102         },
40103         
40104         select : function(index, scrollIntoView)
40105         {
40106             this.selectedIndex = index;
40107             this.view.select(index);
40108             if(scrollIntoView !== false){
40109                 var el = this.view.getNode(index);
40110                 if(el){
40111                     this.list.scrollChildIntoView(el, false);
40112                 }
40113             }
40114         },
40115         
40116         createList : function()
40117         {
40118             this.list = Roo.get(document.body).createChild({
40119                 tag: 'ul',
40120                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40121                 style: 'display:none'
40122             });
40123             
40124             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40125         },
40126         
40127         collapseIf : function(e)
40128         {
40129             var in_combo  = e.within(this.el);
40130             var in_list =  e.within(this.list);
40131             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40132             
40133             if (in_combo || in_list || is_list) {
40134                 return;
40135             }
40136             this.collapse();
40137         },
40138         
40139         onSelect : function(record, index)
40140         {
40141             if(this.fireEvent('beforeselect', this, record, index) !== false){
40142                 
40143                 this.setFlagClass(record.data.iso2);
40144                 this.setDialCode(record.data.dialCode);
40145                 this.hasFocus = false;
40146                 this.collapse();
40147                 this.fireEvent('select', this, record, index);
40148             }
40149         },
40150         
40151         flagEl : function()
40152         {
40153             var flag = this.el.select('div.flag',true).first();
40154             if(!flag){
40155                 return false;
40156             }
40157             return flag;
40158         },
40159         
40160         dialCodeHolderEl : function()
40161         {
40162             var d = this.el.select('input.dial-code-holder',true).first();
40163             if(!d){
40164                 return false;
40165             }
40166             return d;
40167         },
40168         
40169         setDialCode : function(v)
40170         {
40171             this.dialCodeHolder.dom.value = '+'+v;
40172         },
40173         
40174         setFlagClass : function(n)
40175         {
40176             this.flag.dom.className = 'flag '+n;
40177         },
40178         
40179         getValue : function()
40180         {
40181             var v = this.inputEl().getValue();
40182             if(this.dialCodeHolder) {
40183                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40184             }
40185             return v;
40186         },
40187         
40188         setValue : function(v)
40189         {
40190             var d = this.getDialCode(v);
40191             
40192             //invalid dial code
40193             if(v.length == 0 || !d || d.length == 0) {
40194                 if(this.rendered){
40195                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40196                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40197                 }
40198                 return;
40199             }
40200             
40201             //valid dial code
40202             this.setFlagClass(this.dialCodeMapping[d].iso2);
40203             this.setDialCode(d);
40204             this.inputEl().dom.value = v.replace('+'+d,'');
40205             this.hiddenEl().dom.value = this.getValue();
40206             
40207             this.validate();
40208         },
40209         
40210         getDialCode : function(v)
40211         {
40212             v = v ||  '';
40213             
40214             if (v.length == 0) {
40215                 return this.dialCodeHolder.dom.value;
40216             }
40217             
40218             var dialCode = "";
40219             if (v.charAt(0) != "+") {
40220                 return false;
40221             }
40222             var numericChars = "";
40223             for (var i = 1; i < v.length; i++) {
40224               var c = v.charAt(i);
40225               if (!isNaN(c)) {
40226                 numericChars += c;
40227                 if (this.dialCodeMapping[numericChars]) {
40228                   dialCode = v.substr(1, i);
40229                 }
40230                 if (numericChars.length == 4) {
40231                   break;
40232                 }
40233               }
40234             }
40235             return dialCode;
40236         },
40237         
40238         reset : function()
40239         {
40240             this.setValue(this.defaultDialCode);
40241             this.validate();
40242         },
40243         
40244         hiddenEl : function()
40245         {
40246             return this.el.select('input.hidden-tel-input',true).first();
40247         },
40248         
40249         onKeyUp : function(e){
40250             
40251             var k = e.getKey();
40252             var c = e.getCharCode();
40253             
40254             if(
40255                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40256                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40257             ){
40258                 e.stopEvent();
40259             }
40260             
40261             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40262             //     return;
40263             // }
40264             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40265                 e.stopEvent();
40266             }
40267             
40268             this.setValue(this.getValue());
40269         }
40270         
40271 });
40272 /**
40273  * @class Roo.bootstrap.MoneyField
40274  * @extends Roo.bootstrap.ComboBox
40275  * Bootstrap MoneyField class
40276  * 
40277  * @constructor
40278  * Create a new MoneyField.
40279  * @param {Object} config Configuration options
40280  */
40281
40282 Roo.bootstrap.MoneyField = function(config) {
40283     
40284     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40285     
40286 };
40287
40288 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40289     
40290     /**
40291      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40292      */
40293     allowDecimals : true,
40294     /**
40295      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40296      */
40297     decimalSeparator : ".",
40298     /**
40299      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40300      */
40301     decimalPrecision : 0,
40302     /**
40303      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40304      */
40305     allowNegative : true,
40306     /**
40307      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40308      */
40309     allowZero: true,
40310     /**
40311      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40312      */
40313     minValue : Number.NEGATIVE_INFINITY,
40314     /**
40315      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40316      */
40317     maxValue : Number.MAX_VALUE,
40318     /**
40319      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40320      */
40321     minText : "The minimum value for this field is {0}",
40322     /**
40323      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40324      */
40325     maxText : "The maximum value for this field is {0}",
40326     /**
40327      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40328      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40329      */
40330     nanText : "{0} is not a valid number",
40331     /**
40332      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40333      */
40334     castInt : true,
40335     /**
40336      * @cfg {String} defaults currency of the MoneyField
40337      * value should be in lkey
40338      */
40339     defaultCurrency : false,
40340     /**
40341      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40342      */
40343     thousandsDelimiter : false,
40344     
40345     
40346     inputlg : 9,
40347     inputmd : 9,
40348     inputsm : 9,
40349     inputxs : 6,
40350     
40351     store : false,
40352     
40353     getAutoCreate : function()
40354     {
40355         var align = this.labelAlign || this.parentLabelAlign();
40356         
40357         var id = Roo.id();
40358
40359         var cfg = {
40360             cls: 'form-group',
40361             cn: []
40362         };
40363
40364         var input =  {
40365             tag: 'input',
40366             id : id,
40367             cls : 'form-control roo-money-amount-input',
40368             autocomplete: 'new-password'
40369         };
40370         
40371         var hiddenInput = {
40372             tag: 'input',
40373             type: 'hidden',
40374             id: Roo.id(),
40375             cls: 'hidden-number-input'
40376         };
40377         
40378         if (this.name) {
40379             hiddenInput.name = this.name;
40380         }
40381
40382         if (this.disabled) {
40383             input.disabled = true;
40384         }
40385
40386         var clg = 12 - this.inputlg;
40387         var cmd = 12 - this.inputmd;
40388         var csm = 12 - this.inputsm;
40389         var cxs = 12 - this.inputxs;
40390         
40391         var container = {
40392             tag : 'div',
40393             cls : 'row roo-money-field',
40394             cn : [
40395                 {
40396                     tag : 'div',
40397                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40398                     cn : [
40399                         {
40400                             tag : 'div',
40401                             cls: 'roo-select2-container input-group',
40402                             cn: [
40403                                 {
40404                                     tag : 'input',
40405                                     cls : 'form-control roo-money-currency-input',
40406                                     autocomplete: 'new-password',
40407                                     readOnly : 1,
40408                                     name : this.currencyName
40409                                 },
40410                                 {
40411                                     tag :'span',
40412                                     cls : 'input-group-addon',
40413                                     cn : [
40414                                         {
40415                                             tag: 'span',
40416                                             cls: 'caret'
40417                                         }
40418                                     ]
40419                                 }
40420                             ]
40421                         }
40422                     ]
40423                 },
40424                 {
40425                     tag : 'div',
40426                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40427                     cn : [
40428                         {
40429                             tag: 'div',
40430                             cls: this.hasFeedback ? 'has-feedback' : '',
40431                             cn: [
40432                                 input
40433                             ]
40434                         }
40435                     ]
40436                 }
40437             ]
40438             
40439         };
40440         
40441         if (this.fieldLabel.length) {
40442             var indicator = {
40443                 tag: 'i',
40444                 tooltip: 'This field is required'
40445             };
40446
40447             var label = {
40448                 tag: 'label',
40449                 'for':  id,
40450                 cls: 'control-label',
40451                 cn: []
40452             };
40453
40454             var label_text = {
40455                 tag: 'span',
40456                 html: this.fieldLabel
40457             };
40458
40459             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40460             label.cn = [
40461                 indicator,
40462                 label_text
40463             ];
40464
40465             if(this.indicatorpos == 'right') {
40466                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40467                 label.cn = [
40468                     label_text,
40469                     indicator
40470                 ];
40471             }
40472
40473             if(align == 'left') {
40474                 container = {
40475                     tag: 'div',
40476                     cn: [
40477                         container
40478                     ]
40479                 };
40480
40481                 if(this.labelWidth > 12){
40482                     label.style = "width: " + this.labelWidth + 'px';
40483                 }
40484                 if(this.labelWidth < 13 && this.labelmd == 0){
40485                     this.labelmd = this.labelWidth;
40486                 }
40487                 if(this.labellg > 0){
40488                     label.cls += ' col-lg-' + this.labellg;
40489                     input.cls += ' col-lg-' + (12 - this.labellg);
40490                 }
40491                 if(this.labelmd > 0){
40492                     label.cls += ' col-md-' + this.labelmd;
40493                     container.cls += ' col-md-' + (12 - this.labelmd);
40494                 }
40495                 if(this.labelsm > 0){
40496                     label.cls += ' col-sm-' + this.labelsm;
40497                     container.cls += ' col-sm-' + (12 - this.labelsm);
40498                 }
40499                 if(this.labelxs > 0){
40500                     label.cls += ' col-xs-' + this.labelxs;
40501                     container.cls += ' col-xs-' + (12 - this.labelxs);
40502                 }
40503             }
40504         }
40505
40506         cfg.cn = [
40507             label,
40508             container,
40509             hiddenInput
40510         ];
40511         
40512         var settings = this;
40513
40514         ['xs','sm','md','lg'].map(function(size){
40515             if (settings[size]) {
40516                 cfg.cls += ' col-' + size + '-' + settings[size];
40517             }
40518         });
40519         
40520         return cfg;
40521     },
40522     
40523     initEvents : function()
40524     {
40525         this.indicator = this.indicatorEl();
40526         
40527         this.initCurrencyEvent();
40528         
40529         this.initNumberEvent();
40530     },
40531     
40532     initCurrencyEvent : function()
40533     {
40534         if (!this.store) {
40535             throw "can not find store for combo";
40536         }
40537         
40538         this.store = Roo.factory(this.store, Roo.data);
40539         this.store.parent = this;
40540         
40541         this.createList();
40542         
40543         this.triggerEl = this.el.select('.input-group-addon', true).first();
40544         
40545         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40546         
40547         var _this = this;
40548         
40549         (function(){
40550             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40551             _this.list.setWidth(lw);
40552         }).defer(100);
40553         
40554         this.list.on('mouseover', this.onViewOver, this);
40555         this.list.on('mousemove', this.onViewMove, this);
40556         this.list.on('scroll', this.onViewScroll, this);
40557         
40558         if(!this.tpl){
40559             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40560         }
40561         
40562         this.view = new Roo.View(this.list, this.tpl, {
40563             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40564         });
40565         
40566         this.view.on('click', this.onViewClick, this);
40567         
40568         this.store.on('beforeload', this.onBeforeLoad, this);
40569         this.store.on('load', this.onLoad, this);
40570         this.store.on('loadexception', this.onLoadException, this);
40571         
40572         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40573             "up" : function(e){
40574                 this.inKeyMode = true;
40575                 this.selectPrev();
40576             },
40577
40578             "down" : function(e){
40579                 if(!this.isExpanded()){
40580                     this.onTriggerClick();
40581                 }else{
40582                     this.inKeyMode = true;
40583                     this.selectNext();
40584                 }
40585             },
40586
40587             "enter" : function(e){
40588                 this.collapse();
40589                 
40590                 if(this.fireEvent("specialkey", this, e)){
40591                     this.onViewClick(false);
40592                 }
40593                 
40594                 return true;
40595             },
40596
40597             "esc" : function(e){
40598                 this.collapse();
40599             },
40600
40601             "tab" : function(e){
40602                 this.collapse();
40603                 
40604                 if(this.fireEvent("specialkey", this, e)){
40605                     this.onViewClick(false);
40606                 }
40607                 
40608                 return true;
40609             },
40610
40611             scope : this,
40612
40613             doRelay : function(foo, bar, hname){
40614                 if(hname == 'down' || this.scope.isExpanded()){
40615                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40616                 }
40617                 return true;
40618             },
40619
40620             forceKeyDown: true
40621         });
40622         
40623         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40624         
40625     },
40626     
40627     initNumberEvent : function(e)
40628     {
40629         this.inputEl().on("keydown" , this.fireKey,  this);
40630         this.inputEl().on("focus", this.onFocus,  this);
40631         this.inputEl().on("blur", this.onBlur,  this);
40632         
40633         this.inputEl().relayEvent('keyup', this);
40634         
40635         if(this.indicator){
40636             this.indicator.addClass('invisible');
40637         }
40638  
40639         this.originalValue = this.getValue();
40640         
40641         if(this.validationEvent == 'keyup'){
40642             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40643             this.inputEl().on('keyup', this.filterValidation, this);
40644         }
40645         else if(this.validationEvent !== false){
40646             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40647         }
40648         
40649         if(this.selectOnFocus){
40650             this.on("focus", this.preFocus, this);
40651             
40652         }
40653         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40654             this.inputEl().on("keypress", this.filterKeys, this);
40655         } else {
40656             this.inputEl().relayEvent('keypress', this);
40657         }
40658         
40659         var allowed = "0123456789";
40660         
40661         if(this.allowDecimals){
40662             allowed += this.decimalSeparator;
40663         }
40664         
40665         if(this.allowNegative){
40666             allowed += "-";
40667         }
40668         
40669         if(this.thousandsDelimiter) {
40670             allowed += ",";
40671         }
40672         
40673         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40674         
40675         var keyPress = function(e){
40676             
40677             var k = e.getKey();
40678             
40679             var c = e.getCharCode();
40680             
40681             if(
40682                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40683                     allowed.indexOf(String.fromCharCode(c)) === -1
40684             ){
40685                 e.stopEvent();
40686                 return;
40687             }
40688             
40689             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40690                 return;
40691             }
40692             
40693             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40694                 e.stopEvent();
40695             }
40696         };
40697         
40698         this.inputEl().on("keypress", keyPress, this);
40699         
40700     },
40701     
40702     onTriggerClick : function(e)
40703     {   
40704         if(this.disabled){
40705             return;
40706         }
40707         
40708         this.page = 0;
40709         this.loadNext = false;
40710         
40711         if(this.isExpanded()){
40712             this.collapse();
40713             return;
40714         }
40715         
40716         this.hasFocus = true;
40717         
40718         if(this.triggerAction == 'all') {
40719             this.doQuery(this.allQuery, true);
40720             return;
40721         }
40722         
40723         this.doQuery(this.getRawValue());
40724     },
40725     
40726     getCurrency : function()
40727     {   
40728         var v = this.currencyEl().getValue();
40729         
40730         return v;
40731     },
40732     
40733     restrictHeight : function()
40734     {
40735         this.list.alignTo(this.currencyEl(), this.listAlign);
40736         this.list.alignTo(this.currencyEl(), this.listAlign);
40737     },
40738     
40739     onViewClick : function(view, doFocus, el, e)
40740     {
40741         var index = this.view.getSelectedIndexes()[0];
40742         
40743         var r = this.store.getAt(index);
40744         
40745         if(r){
40746             this.onSelect(r, index);
40747         }
40748     },
40749     
40750     onSelect : function(record, index){
40751         
40752         if(this.fireEvent('beforeselect', this, record, index) !== false){
40753         
40754             this.setFromCurrencyData(index > -1 ? record.data : false);
40755             
40756             this.collapse();
40757             
40758             this.fireEvent('select', this, record, index);
40759         }
40760     },
40761     
40762     setFromCurrencyData : function(o)
40763     {
40764         var currency = '';
40765         
40766         this.lastCurrency = o;
40767         
40768         if (this.currencyField) {
40769             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40770         } else {
40771             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40772         }
40773         
40774         this.lastSelectionText = currency;
40775         
40776         //setting default currency
40777         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40778             this.setCurrency(this.defaultCurrency);
40779             return;
40780         }
40781         
40782         this.setCurrency(currency);
40783     },
40784     
40785     setFromData : function(o)
40786     {
40787         var c = {};
40788         
40789         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40790         
40791         this.setFromCurrencyData(c);
40792         
40793         var value = '';
40794         
40795         if (this.name) {
40796             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40797         } else {
40798             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40799         }
40800         
40801         this.setValue(value);
40802         
40803     },
40804     
40805     setCurrency : function(v)
40806     {   
40807         this.currencyValue = v;
40808         
40809         if(this.rendered){
40810             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40811             this.validate();
40812         }
40813     },
40814     
40815     setValue : function(v)
40816     {
40817         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40818         
40819         this.value = v;
40820         
40821         if(this.rendered){
40822             
40823             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40824             
40825             this.inputEl().dom.value = (v == '') ? '' :
40826                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40827             
40828             if(!this.allowZero && v === '0') {
40829                 this.hiddenEl().dom.value = '';
40830                 this.inputEl().dom.value = '';
40831             }
40832             
40833             this.validate();
40834         }
40835     },
40836     
40837     getRawValue : function()
40838     {
40839         var v = this.inputEl().getValue();
40840         
40841         return v;
40842     },
40843     
40844     getValue : function()
40845     {
40846         return this.fixPrecision(this.parseValue(this.getRawValue()));
40847     },
40848     
40849     parseValue : function(value)
40850     {
40851         if(this.thousandsDelimiter) {
40852             value += "";
40853             r = new RegExp(",", "g");
40854             value = value.replace(r, "");
40855         }
40856         
40857         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40858         return isNaN(value) ? '' : value;
40859         
40860     },
40861     
40862     fixPrecision : function(value)
40863     {
40864         if(this.thousandsDelimiter) {
40865             value += "";
40866             r = new RegExp(",", "g");
40867             value = value.replace(r, "");
40868         }
40869         
40870         var nan = isNaN(value);
40871         
40872         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40873             return nan ? '' : value;
40874         }
40875         return parseFloat(value).toFixed(this.decimalPrecision);
40876     },
40877     
40878     decimalPrecisionFcn : function(v)
40879     {
40880         return Math.floor(v);
40881     },
40882     
40883     validateValue : function(value)
40884     {
40885         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40886             return false;
40887         }
40888         
40889         var num = this.parseValue(value);
40890         
40891         if(isNaN(num)){
40892             this.markInvalid(String.format(this.nanText, value));
40893             return false;
40894         }
40895         
40896         if(num < this.minValue){
40897             this.markInvalid(String.format(this.minText, this.minValue));
40898             return false;
40899         }
40900         
40901         if(num > this.maxValue){
40902             this.markInvalid(String.format(this.maxText, this.maxValue));
40903             return false;
40904         }
40905         
40906         return true;
40907     },
40908     
40909     validate : function()
40910     {
40911         if(this.disabled || this.allowBlank){
40912             this.markValid();
40913             return true;
40914         }
40915         
40916         var currency = this.getCurrency();
40917         
40918         if(this.validateValue(this.getRawValue()) && currency.length){
40919             this.markValid();
40920             return true;
40921         }
40922         
40923         this.markInvalid();
40924         return false;
40925     },
40926     
40927     getName: function()
40928     {
40929         return this.name;
40930     },
40931     
40932     beforeBlur : function()
40933     {
40934         if(!this.castInt){
40935             return;
40936         }
40937         
40938         var v = this.parseValue(this.getRawValue());
40939         
40940         if(v || v == 0){
40941             this.setValue(v);
40942         }
40943     },
40944     
40945     onBlur : function()
40946     {
40947         this.beforeBlur();
40948         
40949         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40950             //this.el.removeClass(this.focusClass);
40951         }
40952         
40953         this.hasFocus = false;
40954         
40955         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40956             this.validate();
40957         }
40958         
40959         var v = this.getValue();
40960         
40961         if(String(v) !== String(this.startValue)){
40962             this.fireEvent('change', this, v, this.startValue);
40963         }
40964         
40965         this.fireEvent("blur", this);
40966     },
40967     
40968     inputEl : function()
40969     {
40970         return this.el.select('.roo-money-amount-input', true).first();
40971     },
40972     
40973     currencyEl : function()
40974     {
40975         return this.el.select('.roo-money-currency-input', true).first();
40976     },
40977     
40978     hiddenEl : function()
40979     {
40980         return this.el.select('input.hidden-number-input',true).first();
40981     }
40982     
40983 });