Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
921     setWeight : function(str)
922     {
923         this.el.removeClass(this.weightClass);
924         this.el.addClass('btn-' + str);        
925     }
926     
927     
928 });
929
930  /*
931  * - LGPL
932  *
933  * column
934  * 
935  */
936
937 /**
938  * @class Roo.bootstrap.Column
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Column class
941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
949  *
950  * 
951  * @cfg {Boolean} hidden (true|false) hide the element
952  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953  * @cfg {String} fa (ban|check|...) font awesome icon
954  * @cfg {Number} fasize (1|2|....) font awsome size
955
956  * @cfg {String} icon (info-sign|check|...) glyphicon name
957
958  * @cfg {String} html content of column.
959  * 
960  * @constructor
961  * Create a new Column
962  * @param {Object} config The config object
963  */
964
965 Roo.bootstrap.Column = function(config){
966     Roo.bootstrap.Column.superclass.constructor.call(this, config);
967 };
968
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
970     
971     xs: false,
972     sm: false,
973     md: false,
974     lg: false,
975     xsoff: false,
976     smoff: false,
977     mdoff: false,
978     lgoff: false,
979     html: '',
980     offset: 0,
981     alert: false,
982     fa: false,
983     icon : false,
984     hidden : false,
985     fasize : 1,
986     
987     getAutoCreate : function(){
988         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
989         
990         cfg = {
991             tag: 'div',
992             cls: 'column'
993         };
994         
995         var settings=this;
996         ['xs','sm','md','lg'].map(function(size){
997             //Roo.log( size + ':' + settings[size]);
998             
999             if (settings[size+'off'] !== false) {
1000                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1001             }
1002             
1003             if (settings[size] === false) {
1004                 return;
1005             }
1006             
1007             if (!settings[size]) { // 0 = hidden
1008                 cfg.cls += ' hidden-' + size;
1009                 return;
1010             }
1011             cfg.cls += ' col-' + size + '-' + settings[size];
1012             
1013         });
1014         
1015         if (this.hidden) {
1016             cfg.cls += ' hidden';
1017         }
1018         
1019         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020             cfg.cls +=' alert alert-' + this.alert;
1021         }
1022         
1023         
1024         if (this.html.length) {
1025             cfg.html = this.html;
1026         }
1027         if (this.fa) {
1028             var fasize = '';
1029             if (this.fasize > 1) {
1030                 fasize = ' fa-' + this.fasize + 'x';
1031             }
1032             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1033             
1034             
1035         }
1036         if (this.icon) {
1037             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1038         }
1039         
1040         return cfg;
1041     }
1042    
1043 });
1044
1045  
1046
1047  /*
1048  * - LGPL
1049  *
1050  * page container.
1051  * 
1052  */
1053
1054
1055 /**
1056  * @class Roo.bootstrap.Container
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Container class
1059  * @cfg {Boolean} jumbotron is it a jumbotron element
1060  * @cfg {String} html content of element
1061  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1063  * @cfg {String} header content of header (for panel)
1064  * @cfg {String} footer content of footer (for panel)
1065  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066  * @cfg {String} tag (header|aside|section) type of HTML tag.
1067  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068  * @cfg {String} fa font awesome icon
1069  * @cfg {String} icon (info-sign|check|...) glyphicon name
1070  * @cfg {Boolean} hidden (true|false) hide the element
1071  * @cfg {Boolean} expandable (true|false) default false
1072  * @cfg {Boolean} expanded (true|false) default true
1073  * @cfg {String} rheader contet on the right of header
1074  * @cfg {Boolean} clickable (true|false) default false
1075
1076  *     
1077  * @constructor
1078  * Create a new Container
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Container = function(config){
1083     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // raw events
1087          /**
1088          * @event expand
1089          * After the panel has been expand
1090          * 
1091          * @param {Roo.bootstrap.Container} this
1092          */
1093         "expand" : true,
1094         /**
1095          * @event collapse
1096          * After the panel has been collapsed
1097          * 
1098          * @param {Roo.bootstrap.Container} this
1099          */
1100         "collapse" : true,
1101         /**
1102          * @event click
1103          * When a element is chick
1104          * @param {Roo.bootstrap.Container} this
1105          * @param {Roo.EventObject} e
1106          */
1107         "click" : true
1108     });
1109 };
1110
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1112     
1113     jumbotron : false,
1114     well: '',
1115     panel : '',
1116     header: '',
1117     footer : '',
1118     sticky: '',
1119     tag : false,
1120     alert : false,
1121     fa: false,
1122     icon : false,
1123     expandable : false,
1124     rheader : '',
1125     expanded : true,
1126     clickable: false,
1127   
1128      
1129     getChildContainer : function() {
1130         
1131         if(!this.el){
1132             return false;
1133         }
1134         
1135         if (this.panel.length) {
1136             return this.el.select('.panel-body',true).first();
1137         }
1138         
1139         return this.el;
1140     },
1141     
1142     
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag : this.tag || 'div',
1147             html : '',
1148             cls : ''
1149         };
1150         if (this.jumbotron) {
1151             cfg.cls = 'jumbotron';
1152         }
1153         
1154         
1155         
1156         // - this is applied by the parent..
1157         //if (this.cls) {
1158         //    cfg.cls = this.cls + '';
1159         //}
1160         
1161         if (this.sticky.length) {
1162             
1163             var bd = Roo.get(document.body);
1164             if (!bd.hasClass('bootstrap-sticky')) {
1165                 bd.addClass('bootstrap-sticky');
1166                 Roo.select('html',true).setStyle('height', '100%');
1167             }
1168              
1169             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1170         }
1171         
1172         
1173         if (this.well.length) {
1174             switch (this.well) {
1175                 case 'lg':
1176                 case 'sm':
1177                     cfg.cls +=' well well-' +this.well;
1178                     break;
1179                 default:
1180                     cfg.cls +=' well';
1181                     break;
1182             }
1183         }
1184         
1185         if (this.hidden) {
1186             cfg.cls += ' hidden';
1187         }
1188         
1189         
1190         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191             cfg.cls +=' alert alert-' + this.alert;
1192         }
1193         
1194         var body = cfg;
1195         
1196         if (this.panel.length) {
1197             cfg.cls += ' panel panel-' + this.panel;
1198             cfg.cn = [];
1199             if (this.header.length) {
1200                 
1201                 var h = [];
1202                 
1203                 if(this.expandable){
1204                     
1205                     cfg.cls = cfg.cls + ' expandable';
1206                     
1207                     h.push({
1208                         tag: 'i',
1209                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1210                     });
1211                     
1212                 }
1213                 
1214                 h.push(
1215                     {
1216                         tag: 'span',
1217                         cls : 'panel-title',
1218                         html : (this.expandable ? '&nbsp;' : '') + this.header
1219                     },
1220                     {
1221                         tag: 'span',
1222                         cls: 'panel-header-right',
1223                         html: this.rheader
1224                     }
1225                 );
1226                 
1227                 cfg.cn.push({
1228                     cls : 'panel-heading',
1229                     style : this.expandable ? 'cursor: pointer' : '',
1230                     cn : h
1231                 });
1232                 
1233             }
1234             
1235             body = false;
1236             cfg.cn.push({
1237                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1238                 html : this.html
1239             });
1240             
1241             
1242             if (this.footer.length) {
1243                 cfg.cn.push({
1244                     cls : 'panel-footer',
1245                     html : this.footer
1246                     
1247                 });
1248             }
1249             
1250         }
1251         
1252         if (body) {
1253             body.html = this.html || cfg.html;
1254             // prefix with the icons..
1255             if (this.fa) {
1256                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1257             }
1258             if (this.icon) {
1259                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1260             }
1261             
1262             
1263         }
1264         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265             cfg.cls =  'container';
1266         }
1267         
1268         return cfg;
1269     },
1270     
1271     initEvents: function() 
1272     {
1273         if(this.expandable){
1274             var headerEl = this.headerEl();
1275         
1276             if(headerEl){
1277                 headerEl.on('click', this.onToggleClick, this);
1278             }
1279         }
1280         
1281         if(this.clickable){
1282             this.el.on('click', this.onClick, this);
1283         }
1284         
1285     },
1286     
1287     onToggleClick : function()
1288     {
1289         var headerEl = this.headerEl();
1290         
1291         if(!headerEl){
1292             return;
1293         }
1294         
1295         if(this.expanded){
1296             this.collapse();
1297             return;
1298         }
1299         
1300         this.expand();
1301     },
1302     
1303     expand : function()
1304     {
1305         if(this.fireEvent('expand', this)) {
1306             
1307             this.expanded = true;
1308             
1309             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1310             
1311             this.el.select('.panel-body',true).first().removeClass('hide');
1312             
1313             var toggleEl = this.toggleEl();
1314
1315             if(!toggleEl){
1316                 return;
1317             }
1318
1319             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1320         }
1321         
1322     },
1323     
1324     collapse : function()
1325     {
1326         if(this.fireEvent('collapse', this)) {
1327             
1328             this.expanded = false;
1329             
1330             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331             this.el.select('.panel-body',true).first().addClass('hide');
1332         
1333             var toggleEl = this.toggleEl();
1334
1335             if(!toggleEl){
1336                 return;
1337             }
1338
1339             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1340         }
1341     },
1342     
1343     toggleEl : function()
1344     {
1345         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1346             return;
1347         }
1348         
1349         return this.el.select('.panel-heading .fa',true).first();
1350     },
1351     
1352     headerEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading',true).first()
1359     },
1360     
1361     bodyEl : function()
1362     {
1363         if(!this.el || !this.panel.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-body',true).first()
1368     },
1369     
1370     titleEl : function()
1371     {
1372         if(!this.el || !this.panel.length || !this.header.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-title',true).first();
1377     },
1378     
1379     setTitle : function(v)
1380     {
1381         var titleEl = this.titleEl();
1382         
1383         if(!titleEl){
1384             return;
1385         }
1386         
1387         titleEl.dom.innerHTML = v;
1388     },
1389     
1390     getTitle : function()
1391     {
1392         
1393         var titleEl = this.titleEl();
1394         
1395         if(!titleEl){
1396             return '';
1397         }
1398         
1399         return titleEl.dom.innerHTML;
1400     },
1401     
1402     setRightTitle : function(v)
1403     {
1404         var t = this.el.select('.panel-header-right',true).first();
1405         
1406         if(!t){
1407             return;
1408         }
1409         
1410         t.dom.innerHTML = v;
1411     },
1412     
1413     onClick : function(e)
1414     {
1415         e.preventDefault();
1416         
1417         this.fireEvent('click', this, e);
1418     }
1419 });
1420
1421  /*
1422  * - LGPL
1423  *
1424  * image
1425  * 
1426  */
1427
1428
1429 /**
1430  * @class Roo.bootstrap.Img
1431  * @extends Roo.bootstrap.Component
1432  * Bootstrap Img class
1433  * @cfg {Boolean} imgResponsive false | true
1434  * @cfg {String} border rounded | circle | thumbnail
1435  * @cfg {String} src image source
1436  * @cfg {String} alt image alternative text
1437  * @cfg {String} href a tag href
1438  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439  * @cfg {String} xsUrl xs image source
1440  * @cfg {String} smUrl sm image source
1441  * @cfg {String} mdUrl md image source
1442  * @cfg {String} lgUrl lg image source
1443  * 
1444  * @constructor
1445  * Create a new Input
1446  * @param {Object} config The config object
1447  */
1448
1449 Roo.bootstrap.Img = function(config){
1450     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451     
1452     this.addEvents({
1453         // img events
1454         /**
1455          * @event click
1456          * The img click event for the img.
1457          * @param {Roo.EventObject} e
1458          */
1459         "click" : true
1460     });
1461 };
1462
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1464     
1465     imgResponsive: true,
1466     border: '',
1467     src: 'about:blank',
1468     href: false,
1469     target: false,
1470     xsUrl: '',
1471     smUrl: '',
1472     mdUrl: '',
1473     lgUrl: '',
1474
1475     getAutoCreate : function()
1476     {   
1477         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478             return this.createSingleImg();
1479         }
1480         
1481         var cfg = {
1482             tag: 'div',
1483             cls: 'roo-image-responsive-group',
1484             cn: []
1485         };
1486         var _this = this;
1487         
1488         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1489             
1490             if(!_this[size + 'Url']){
1491                 return;
1492             }
1493             
1494             var img = {
1495                 tag: 'img',
1496                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497                 html: _this.html || cfg.html,
1498                 src: _this[size + 'Url']
1499             };
1500             
1501             img.cls += ' roo-image-responsive-' + size;
1502             
1503             var s = ['xs', 'sm', 'md', 'lg'];
1504             
1505             s.splice(s.indexOf(size), 1);
1506             
1507             Roo.each(s, function(ss){
1508                 img.cls += ' hidden-' + ss;
1509             });
1510             
1511             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512                 cfg.cls += ' img-' + _this.border;
1513             }
1514             
1515             if(_this.alt){
1516                 cfg.alt = _this.alt;
1517             }
1518             
1519             if(_this.href){
1520                 var a = {
1521                     tag: 'a',
1522                     href: _this.href,
1523                     cn: [
1524                         img
1525                     ]
1526                 };
1527
1528                 if(this.target){
1529                     a.target = _this.target;
1530                 }
1531             }
1532             
1533             cfg.cn.push((_this.href) ? a : img);
1534             
1535         });
1536         
1537         return cfg;
1538     },
1539     
1540     createSingleImg : function()
1541     {
1542         var cfg = {
1543             tag: 'img',
1544             cls: (this.imgResponsive) ? 'img-responsive' : '',
1545             html : null,
1546             src : 'about:blank'  // just incase src get's set to undefined?!?
1547         };
1548         
1549         cfg.html = this.html || cfg.html;
1550         
1551         cfg.src = this.src || cfg.src;
1552         
1553         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554             cfg.cls += ' img-' + this.border;
1555         }
1556         
1557         if(this.alt){
1558             cfg.alt = this.alt;
1559         }
1560         
1561         if(this.href){
1562             var a = {
1563                 tag: 'a',
1564                 href: this.href,
1565                 cn: [
1566                     cfg
1567                 ]
1568             };
1569             
1570             if(this.target){
1571                 a.target = this.target;
1572             }
1573             
1574         }
1575         
1576         return (this.href) ? a : cfg;
1577     },
1578     
1579     initEvents: function() 
1580     {
1581         if(!this.href){
1582             this.el.on('click', this.onClick, this);
1583         }
1584         
1585     },
1586     
1587     onClick : function(e)
1588     {
1589         Roo.log('img onclick');
1590         this.fireEvent('click', this, e);
1591     },
1592     /**
1593      * Sets the url of the image - used to update it
1594      * @param {String} url the url of the image
1595      */
1596     
1597     setSrc : function(url)
1598     {
1599         this.src =  url;
1600         
1601         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602             this.el.dom.src =  url;
1603             return;
1604         }
1605         
1606         this.el.select('img', true).first().dom.src =  url;
1607     }
1608     
1609     
1610    
1611 });
1612
1613  /*
1614  * - LGPL
1615  *
1616  * image
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.Link
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap Link Class
1625  * @cfg {String} alt image alternative text
1626  * @cfg {String} href a tag href
1627  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628  * @cfg {String} html the content of the link.
1629  * @cfg {String} anchor name for the anchor link
1630  * @cfg {String} fa - favicon
1631
1632  * @cfg {Boolean} preventDefault (true | false) default false
1633
1634  * 
1635  * @constructor
1636  * Create a new Input
1637  * @param {Object} config The config object
1638  */
1639
1640 Roo.bootstrap.Link = function(config){
1641     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642     
1643     this.addEvents({
1644         // img events
1645         /**
1646          * @event click
1647          * The img click event for the img.
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1655     
1656     href: false,
1657     target: false,
1658     preventDefault: false,
1659     anchor : false,
1660     alt : false,
1661     fa: false,
1662
1663
1664     getAutoCreate : function()
1665     {
1666         var html = this.html || '';
1667         
1668         if (this.fa !== false) {
1669             html = '<i class="fa fa-' + this.fa + '"></i>';
1670         }
1671         var cfg = {
1672             tag: 'a'
1673         };
1674         // anchor's do not require html/href...
1675         if (this.anchor === false) {
1676             cfg.html = html;
1677             cfg.href = this.href || '#';
1678         } else {
1679             cfg.name = this.anchor;
1680             if (this.html !== false || this.fa !== false) {
1681                 cfg.html = html;
1682             }
1683             if (this.href !== false) {
1684                 cfg.href = this.href;
1685             }
1686         }
1687         
1688         if(this.alt !== false){
1689             cfg.alt = this.alt;
1690         }
1691         
1692         
1693         if(this.target !== false) {
1694             cfg.target = this.target;
1695         }
1696         
1697         return cfg;
1698     },
1699     
1700     initEvents: function() {
1701         
1702         if(!this.href || this.preventDefault){
1703             this.el.on('click', this.onClick, this);
1704         }
1705     },
1706     
1707     onClick : function(e)
1708     {
1709         if(this.preventDefault){
1710             e.preventDefault();
1711         }
1712         //Roo.log('img onclick');
1713         this.fireEvent('click', this, e);
1714     }
1715    
1716 });
1717
1718  /*
1719  * - LGPL
1720  *
1721  * header
1722  * 
1723  */
1724
1725 /**
1726  * @class Roo.bootstrap.Header
1727  * @extends Roo.bootstrap.Component
1728  * Bootstrap Header class
1729  * @cfg {String} html content of header
1730  * @cfg {Number} level (1|2|3|4|5|6) default 1
1731  * 
1732  * @constructor
1733  * Create a new Header
1734  * @param {Object} config The config object
1735  */
1736
1737
1738 Roo.bootstrap.Header  = function(config){
1739     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1740 };
1741
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1743     
1744     //href : false,
1745     html : false,
1746     level : 1,
1747     
1748     
1749     
1750     getAutoCreate : function(){
1751         
1752         
1753         
1754         var cfg = {
1755             tag: 'h' + (1 *this.level),
1756             html: this.html || ''
1757         } ;
1758         
1759         return cfg;
1760     }
1761    
1762 });
1763
1764  
1765
1766  /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776  
1777 /**
1778  * @class Roo.bootstrap.MenuMgr
1779  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1780  * @singleton
1781  */
1782 Roo.bootstrap.MenuMgr = function(){
1783    var menus, active, groups = {}, attached = false, lastShow = new Date();
1784
1785    // private - called when first menu is created
1786    function init(){
1787        menus = {};
1788        active = new Roo.util.MixedCollection();
1789        Roo.get(document).addKeyListener(27, function(){
1790            if(active.length > 0){
1791                hideAll();
1792            }
1793        });
1794    }
1795
1796    // private
1797    function hideAll(){
1798        if(active && active.length > 0){
1799            var c = active.clone();
1800            c.each(function(m){
1801                m.hide();
1802            });
1803        }
1804    }
1805
1806    // private
1807    function onHide(m){
1808        active.remove(m);
1809        if(active.length < 1){
1810            Roo.get(document).un("mouseup", onMouseDown);
1811             
1812            attached = false;
1813        }
1814    }
1815
1816    // private
1817    function onShow(m){
1818        var last = active.last();
1819        lastShow = new Date();
1820        active.add(m);
1821        if(!attached){
1822           Roo.get(document).on("mouseup", onMouseDown);
1823            
1824            attached = true;
1825        }
1826        if(m.parentMenu){
1827           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828           m.parentMenu.activeChild = m;
1829        }else if(last && last.isVisible()){
1830           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1831        }
1832    }
1833
1834    // private
1835    function onBeforeHide(m){
1836        if(m.activeChild){
1837            m.activeChild.hide();
1838        }
1839        if(m.autoHideTimer){
1840            clearTimeout(m.autoHideTimer);
1841            delete m.autoHideTimer;
1842        }
1843    }
1844
1845    // private
1846    function onBeforeShow(m){
1847        var pm = m.parentMenu;
1848        if(!pm && !m.allowOtherMenus){
1849            hideAll();
1850        }else if(pm && pm.activeChild && active != m){
1851            pm.activeChild.hide();
1852        }
1853    }
1854
1855    // private this should really trigger on mouseup..
1856    function onMouseDown(e){
1857         Roo.log("on Mouse Up");
1858         
1859         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860             Roo.log("MenuManager hideAll");
1861             hideAll();
1862             e.stopEvent();
1863         }
1864         
1865         
1866    }
1867
1868    // private
1869    function onBeforeCheck(mi, state){
1870        if(state){
1871            var g = groups[mi.group];
1872            for(var i = 0, l = g.length; i < l; i++){
1873                if(g[i] != mi){
1874                    g[i].setChecked(false);
1875                }
1876            }
1877        }
1878    }
1879
1880    return {
1881
1882        /**
1883         * Hides all menus that are currently visible
1884         */
1885        hideAll : function(){
1886             hideAll();  
1887        },
1888
1889        // private
1890        register : function(menu){
1891            if(!menus){
1892                init();
1893            }
1894            menus[menu.id] = menu;
1895            menu.on("beforehide", onBeforeHide);
1896            menu.on("hide", onHide);
1897            menu.on("beforeshow", onBeforeShow);
1898            menu.on("show", onShow);
1899            var g = menu.group;
1900            if(g && menu.events["checkchange"]){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menu);
1905                menu.on("checkchange", onCheck);
1906            }
1907        },
1908
1909         /**
1910          * Returns a {@link Roo.menu.Menu} object
1911          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912          * be used to generate and return a new Menu instance.
1913          */
1914        get : function(menu){
1915            if(typeof menu == "string"){ // menu id
1916                return menus[menu];
1917            }else if(menu.events){  // menu instance
1918                return menu;
1919            }
1920            /*else if(typeof menu.length == 'number'){ // array of menu items?
1921                return new Roo.bootstrap.Menu({items:menu});
1922            }else{ // otherwise, must be a config
1923                return new Roo.bootstrap.Menu(menu);
1924            }
1925            */
1926            return false;
1927        },
1928
1929        // private
1930        unregister : function(menu){
1931            delete menus[menu.id];
1932            menu.un("beforehide", onBeforeHide);
1933            menu.un("hide", onHide);
1934            menu.un("beforeshow", onBeforeShow);
1935            menu.un("show", onShow);
1936            var g = menu.group;
1937            if(g && menu.events["checkchange"]){
1938                groups[g].remove(menu);
1939                menu.un("checkchange", onCheck);
1940            }
1941        },
1942
1943        // private
1944        registerCheckable : function(menuItem){
1945            var g = menuItem.group;
1946            if(g){
1947                if(!groups[g]){
1948                    groups[g] = [];
1949                }
1950                groups[g].push(menuItem);
1951                menuItem.on("beforecheckchange", onBeforeCheck);
1952            }
1953        },
1954
1955        // private
1956        unregisterCheckable : function(menuItem){
1957            var g = menuItem.group;
1958            if(g){
1959                groups[g].remove(menuItem);
1960                menuItem.un("beforecheckchange", onBeforeCheck);
1961            }
1962        }
1963    };
1964 }();/*
1965  * - LGPL
1966  *
1967  * menu
1968  * 
1969  */
1970
1971 /**
1972  * @class Roo.bootstrap.Menu
1973  * @extends Roo.bootstrap.Component
1974  * Bootstrap Menu class - container for MenuItems
1975  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1977  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1978  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1979  * 
1980  * @constructor
1981  * Create a new Menu
1982  * @param {Object} config The config object
1983  */
1984
1985
1986 Roo.bootstrap.Menu = function(config){
1987     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988     if (this.registerMenu && this.type != 'treeview')  {
1989         Roo.bootstrap.MenuMgr.register(this);
1990     }
1991     this.addEvents({
1992         /**
1993          * @event beforeshow
1994          * Fires before this menu is displayed
1995          * @param {Roo.menu.Menu} this
1996          */
1997         beforeshow : true,
1998         /**
1999          * @event beforehide
2000          * Fires before this menu is hidden
2001          * @param {Roo.menu.Menu} this
2002          */
2003         beforehide : true,
2004         /**
2005          * @event show
2006          * Fires after this menu is displayed
2007          * @param {Roo.menu.Menu} this
2008          */
2009         show : true,
2010         /**
2011          * @event hide
2012          * Fires after this menu is hidden
2013          * @param {Roo.menu.Menu} this
2014          */
2015         hide : true,
2016         /**
2017          * @event click
2018          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021          * @param {Roo.EventObject} e
2022          */
2023         click : true,
2024         /**
2025          * @event mouseover
2026          * Fires when the mouse is hovering over this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseover : true,
2032         /**
2033          * @event mouseout
2034          * Fires when the mouse exits this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseout : true,
2040         /**
2041          * @event itemclick
2042          * Fires when a menu item contained in this menu is clicked
2043          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044          * @param {Roo.EventObject} e
2045          */
2046         itemclick: true
2047     });
2048     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2049 };
2050
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2052     
2053    /// html : false,
2054     //align : '',
2055     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2056     type: false,
2057     /**
2058      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2059      */
2060     registerMenu : true,
2061     
2062     menuItems :false, // stores the menu items..
2063     
2064     hidden:true,
2065         
2066     parentMenu : false,
2067     
2068     stopEvent : true,
2069     
2070     isLink : false,
2071     
2072     getChildContainer : function() {
2073         return this.el;  
2074     },
2075     
2076     getAutoCreate : function(){
2077          
2078         //if (['right'].indexOf(this.align)!==-1) {
2079         //    cfg.cn[1].cls += ' pull-right'
2080         //}
2081         
2082         
2083         var cfg = {
2084             tag : 'ul',
2085             cls : 'dropdown-menu' ,
2086             style : 'z-index:1000'
2087             
2088         };
2089         
2090         if (this.type === 'submenu') {
2091             cfg.cls = 'submenu active';
2092         }
2093         if (this.type === 'treeview') {
2094             cfg.cls = 'treeview-menu';
2095         }
2096         
2097         return cfg;
2098     },
2099     initEvents : function() {
2100         
2101        // Roo.log("ADD event");
2102        // Roo.log(this.triggerEl.dom);
2103         
2104         this.triggerEl.on('click', this.onTriggerClick, this);
2105         
2106         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2107         
2108         this.triggerEl.addClass('dropdown-toggle');
2109         
2110         if (Roo.isTouch) {
2111             this.el.on('touchstart'  , this.onTouch, this);
2112         }
2113         this.el.on('click' , this.onClick, this);
2114
2115         this.el.on("mouseover", this.onMouseOver, this);
2116         this.el.on("mouseout", this.onMouseOut, this);
2117         
2118     },
2119     
2120     findTargetItem : function(e)
2121     {
2122         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2123         if(!t){
2124             return false;
2125         }
2126         //Roo.log(t);         Roo.log(t.id);
2127         if(t && t.id){
2128             //Roo.log(this.menuitems);
2129             return this.menuitems.get(t.id);
2130             
2131             //return this.items.get(t.menuItemId);
2132         }
2133         
2134         return false;
2135     },
2136     
2137     onTouch : function(e) 
2138     {
2139         Roo.log("menu.onTouch");
2140         //e.stopEvent(); this make the user popdown broken
2141         this.onClick(e);
2142     },
2143     
2144     onClick : function(e)
2145     {
2146         Roo.log("menu.onClick");
2147         
2148         var t = this.findTargetItem(e);
2149         if(!t || t.isContainer){
2150             return;
2151         }
2152         Roo.log(e);
2153         /*
2154         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2155             if(t == this.activeItem && t.shouldDeactivate(e)){
2156                 this.activeItem.deactivate();
2157                 delete this.activeItem;
2158                 return;
2159             }
2160             if(t.canActivate){
2161                 this.setActiveItem(t, true);
2162             }
2163             return;
2164             
2165             
2166         }
2167         */
2168        
2169         Roo.log('pass click event');
2170         
2171         t.onClick(e);
2172         
2173         this.fireEvent("click", this, t, e);
2174         
2175         var _this = this;
2176         
2177         if(!t.href.length || t.href == '#'){
2178             (function() { _this.hide(); }).defer(100);
2179         }
2180         
2181     },
2182     
2183     onMouseOver : function(e){
2184         var t  = this.findTargetItem(e);
2185         //Roo.log(t);
2186         //if(t){
2187         //    if(t.canActivate && !t.disabled){
2188         //        this.setActiveItem(t, true);
2189         //    }
2190         //}
2191         
2192         this.fireEvent("mouseover", this, e, t);
2193     },
2194     isVisible : function(){
2195         return !this.hidden;
2196     },
2197      onMouseOut : function(e){
2198         var t  = this.findTargetItem(e);
2199         
2200         //if(t ){
2201         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2202         //        this.activeItem.deactivate();
2203         //        delete this.activeItem;
2204         //    }
2205         //}
2206         this.fireEvent("mouseout", this, e, t);
2207     },
2208     
2209     
2210     /**
2211      * Displays this menu relative to another element
2212      * @param {String/HTMLElement/Roo.Element} element The element to align to
2213      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214      * the element (defaults to this.defaultAlign)
2215      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2216      */
2217     show : function(el, pos, parentMenu){
2218         this.parentMenu = parentMenu;
2219         if(!this.el){
2220             this.render();
2221         }
2222         this.fireEvent("beforeshow", this);
2223         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2224     },
2225      /**
2226      * Displays this menu at a specific xy position
2227      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     showAt : function(xy, parentMenu, /* private: */_e){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         if(_e !== false){
2236             this.fireEvent("beforeshow", this);
2237             //xy = this.el.adjustForConstraints(xy);
2238         }
2239         
2240         //this.el.show();
2241         this.hideMenuItems();
2242         this.hidden = false;
2243         this.triggerEl.addClass('open');
2244         
2245         // reassign x when hitting right
2246         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2248         }
2249         
2250         // reassign y when hitting bottom
2251         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2253         }
2254         
2255         // but the list may align on trigger left or trigger top... should it be a properity?
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  * @cfg {Number} max_width set the max width of modal
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628     
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637     
2638     max_width: 0,
2639     
2640     max_height: 0,
2641     
2642     fit_content: false,
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         
2681         //this.el.addClass("x-dlg-modal");
2682
2683         if (this.buttons.length) {
2684             Roo.each(this.buttons, function(bb) {
2685                 var b = Roo.apply({}, bb);
2686                 b.xns = b.xns || Roo.bootstrap;
2687                 b.xtype = b.xtype || 'Button';
2688                 if (typeof(b.listeners) == 'undefined') {
2689                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2690                 }
2691
2692                 var btn = Roo.factory(b);
2693
2694                 btn.render(this.el.select('.modal-footer div').first());
2695
2696             },this);
2697         }
2698         // render the children.
2699         var nitems = [];
2700
2701         if(typeof(this.items) != 'undefined'){
2702             var items = this.items;
2703             delete this.items;
2704
2705             for(var i =0;i < items.length;i++) {
2706                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2707             }
2708         }
2709
2710         this.items = nitems;
2711
2712         // where are these used - they used to be body/close/footer
2713
2714
2715         this.initEvents();
2716         //this.el.addClass([this.fieldClass, this.cls]);
2717
2718     },
2719
2720     getAutoCreate : function()
2721     {
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(
2819             Roo.lib.Dom.getViewWidth(true),
2820             Roo.lib.Dom.getViewHeight(true)
2821         );
2822         
2823         if (this.fitwindow) {
2824             this.setSize(
2825                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2827             );
2828             return;
2829         }
2830         
2831         if(this.max_width !== 0) {
2832             
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             if(this.height) {
2836                 this.setSize(w, this.height);
2837                 return;
2838             }
2839             
2840             if(this.max_height) {
2841                 this.setSize(w,Math.min(
2842                     this.max_height,
2843                     Roo.lib.Dom.getViewportHeight(true) - 60
2844                 ));
2845                 
2846                 return;
2847             }
2848             
2849             if(!this.fit_content) {
2850                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2851                 return;
2852             }
2853             
2854             this.setSize(w, Math.min(
2855                 60 +
2856                 this.headerEl.getHeight() + 
2857                 this.footerEl.getHeight() + 
2858                 this.getChildHeight(this.bodyEl.dom.childNodes),
2859                 Roo.lib.Dom.getViewportHeight(true) - 60)
2860             );
2861         }
2862         
2863     },
2864
2865     setSize : function(w,h)
2866     {
2867         if (!w && !h) {
2868             return;
2869         }
2870         
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h - this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     },
3024     
3025     getChildHeight : function(child_nodes)
3026     {
3027         if(
3028             !child_nodes ||
3029             child_nodes.length == 0
3030         ) {
3031             return;
3032         }
3033         
3034         var child_height = 0;
3035         
3036         for(var i = 0; i < child_nodes.length; i++) {
3037             
3038             /*
3039             * for modal with tabs...
3040             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3041                 
3042                 var layout_childs = child_nodes[i].childNodes;
3043                 
3044                 for(var j = 0; j < layout_childs.length; j++) {
3045                     
3046                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3047                         
3048                         var layout_body_childs = layout_childs[j].childNodes;
3049                         
3050                         for(var k = 0; k < layout_body_childs.length; k++) {
3051                             
3052                             if(layout_body_childs[k].classList.contains('navbar')) {
3053                                 child_height += layout_body_childs[k].offsetHeight;
3054                                 continue;
3055                             }
3056                             
3057                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3058                                 
3059                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3060                                 
3061                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3062                                     
3063                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3065                                         continue;
3066                                     }
3067                                     
3068                                 }
3069                                 
3070                             }
3071                             
3072                         }
3073                     }
3074                 }
3075                 continue;
3076             }
3077             */
3078             
3079             child_height += child_nodes[i].offsetHeight;
3080             // Roo.log(child_nodes[i].offsetHeight);
3081         }
3082         
3083         return child_height;
3084     }
3085
3086 });
3087
3088
3089 Roo.apply(Roo.bootstrap.Modal,  {
3090     /**
3091          * Button config that displays a single OK button
3092          * @type Object
3093          */
3094         OK :  [{
3095             name : 'ok',
3096             weight : 'primary',
3097             html : 'OK'
3098         }],
3099         /**
3100          * Button config that displays Yes and No buttons
3101          * @type Object
3102          */
3103         YESNO : [
3104             {
3105                 name  : 'no',
3106                 html : 'No'
3107             },
3108             {
3109                 name  :'yes',
3110                 weight : 'primary',
3111                 html : 'Yes'
3112             }
3113         ],
3114
3115         /**
3116          * Button config that displays OK and Cancel buttons
3117          * @type Object
3118          */
3119         OKCANCEL : [
3120             {
3121                name : 'cancel',
3122                 html : 'Cancel'
3123             },
3124             {
3125                 name : 'ok',
3126                 weight : 'primary',
3127                 html : 'OK'
3128             }
3129         ],
3130         /**
3131          * Button config that displays Yes, No and Cancel buttons
3132          * @type Object
3133          */
3134         YESNOCANCEL : [
3135             {
3136                 name : 'yes',
3137                 weight : 'primary',
3138                 html : 'Yes'
3139             },
3140             {
3141                 name : 'no',
3142                 html : 'No'
3143             },
3144             {
3145                 name : 'cancel',
3146                 html : 'Cancel'
3147             }
3148         ],
3149         
3150         zIndex : 10001
3151 });
3152 /*
3153  * - LGPL
3154  *
3155  * messagebox - can be used as a replace
3156  * 
3157  */
3158 /**
3159  * @class Roo.MessageBox
3160  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3161  * Example usage:
3162  *<pre><code>
3163 // Basic alert:
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3165
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3168     if (btn == 'ok'){
3169         // process text value...
3170     }
3171 });
3172
3173 // Show a dialog using config options:
3174 Roo.Msg.show({
3175    title:'Save Changes?',
3176    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177    buttons: Roo.Msg.YESNOCANCEL,
3178    fn: processResult,
3179    animEl: 'elId'
3180 });
3181 </code></pre>
3182  * @singleton
3183  */
3184 Roo.bootstrap.MessageBox = function(){
3185     var dlg, opt, mask, waitTimer;
3186     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187     var buttons, activeTextEl, bwidth;
3188
3189     
3190     // private
3191     var handleButton = function(button){
3192         dlg.hide();
3193         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3194     };
3195
3196     // private
3197     var handleHide = function(){
3198         if(opt && opt.cls){
3199             dlg.el.removeClass(opt.cls);
3200         }
3201         //if(waitTimer){
3202         //    Roo.TaskMgr.stop(waitTimer);
3203         //    waitTimer = null;
3204         //}
3205     };
3206
3207     // private
3208     var updateButtons = function(b){
3209         var width = 0;
3210         if(!b){
3211             buttons["ok"].hide();
3212             buttons["cancel"].hide();
3213             buttons["yes"].hide();
3214             buttons["no"].hide();
3215             //dlg.footer.dom.style.display = 'none';
3216             return width;
3217         }
3218         dlg.footerEl.dom.style.display = '';
3219         for(var k in buttons){
3220             if(typeof buttons[k] != "function"){
3221                 if(b[k]){
3222                     buttons[k].show();
3223                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224                     width += buttons[k].el.getWidth()+15;
3225                 }else{
3226                     buttons[k].hide();
3227                 }
3228             }
3229         }
3230         return width;
3231     };
3232
3233     // private
3234     var handleEsc = function(d, k, e){
3235         if(opt && opt.closable !== false){
3236             dlg.hide();
3237         }
3238         if(e){
3239             e.stopEvent();
3240         }
3241     };
3242
3243     return {
3244         /**
3245          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246          * @return {Roo.BasicDialog} The BasicDialog element
3247          */
3248         getDialog : function(){
3249            if(!dlg){
3250                 dlg = new Roo.bootstrap.Modal( {
3251                     //draggable: true,
3252                     //resizable:false,
3253                     //constraintoviewport:false,
3254                     //fixedcenter:true,
3255                     //collapsible : false,
3256                     //shim:true,
3257                     //modal: true,
3258                 //    width: 'auto',
3259                   //  height:100,
3260                     //buttonAlign:"center",
3261                     closeClick : function(){
3262                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3263                             handleButton("no");
3264                         }else{
3265                             handleButton("cancel");
3266                         }
3267                     }
3268                 });
3269                 dlg.render();
3270                 dlg.on("hide", handleHide);
3271                 mask = dlg.mask;
3272                 //dlg.addKeyListener(27, handleEsc);
3273                 buttons = {};
3274                 this.buttons = buttons;
3275                 var bt = this.buttonText;
3276                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3280                 //Roo.log(buttons);
3281                 bodyEl = dlg.bodyEl.createChild({
3282
3283                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284                         '<textarea class="roo-mb-textarea"></textarea>' +
3285                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3286                 });
3287                 msgEl = bodyEl.dom.firstChild;
3288                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289                 textboxEl.enableDisplayMode();
3290                 textboxEl.addKeyListener([10,13], function(){
3291                     if(dlg.isVisible() && opt && opt.buttons){
3292                         if(opt.buttons.ok){
3293                             handleButton("ok");
3294                         }else if(opt.buttons.yes){
3295                             handleButton("yes");
3296                         }
3297                     }
3298                 });
3299                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300                 textareaEl.enableDisplayMode();
3301                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302                 progressEl.enableDisplayMode();
3303                 
3304                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305                 var pf = progressEl.dom.firstChild;
3306                 if (pf) {
3307                     pp = Roo.get(pf.firstChild);
3308                     pp.setHeight(pf.offsetHeight);
3309                 }
3310                 
3311             }
3312             return dlg;
3313         },
3314
3315         /**
3316          * Updates the message box body text
3317          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318          * the XHTML-compliant non-breaking space character '&amp;#160;')
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         updateText : function(text)
3322         {
3323             if(!dlg.isVisible() && !opt.width){
3324                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3326             }
3327             msgEl.innerHTML = text || '&#160;';
3328       
3329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3331             var w = Math.max(
3332                     Math.min(opt.width || cw , this.maxWidth), 
3333                     Math.max(opt.minWidth || this.minWidth, bwidth)
3334             );
3335             if(opt.prompt){
3336                 activeTextEl.setWidth(w);
3337             }
3338             if(dlg.isVisible()){
3339                 dlg.fixedcenter = false;
3340             }
3341             // to big, make it scroll. = But as usual stupid IE does not support
3342             // !important..
3343             
3344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3347             } else {
3348                 bodyEl.dom.style.height = '';
3349                 bodyEl.dom.style.overflowY = '';
3350             }
3351             if (cw > w) {
3352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3353             } else {
3354                 bodyEl.dom.style.overflowX = '';
3355             }
3356             
3357             dlg.setContentSize(w, bodyEl.getHeight());
3358             if(dlg.isVisible()){
3359                 dlg.fixedcenter = true;
3360             }
3361             return this;
3362         },
3363
3364         /**
3365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         updateProgress : function(value, text){
3372             if(text){
3373                 this.updateText(text);
3374             }
3375             
3376             if (pp) { // weird bug on my firefox - for some reason this is not defined
3377                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3379             }
3380             return this;
3381         },        
3382
3383         /**
3384          * Returns true if the message box is currently displayed
3385          * @return {Boolean} True if the message box is visible, else false
3386          */
3387         isVisible : function(){
3388             return dlg && dlg.isVisible();  
3389         },
3390
3391         /**
3392          * Hides the message box if it is displayed
3393          */
3394         hide : function(){
3395             if(this.isVisible()){
3396                 dlg.hide();
3397             }  
3398         },
3399
3400         /**
3401          * Displays a new message box, or reinitializes an existing message box, based on the config options
3402          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403          * The following config object properties are supported:
3404          * <pre>
3405 Property    Type             Description
3406 ----------  ---------------  ------------------------------------------------------------------------------------
3407 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3408                                    closes (defaults to undefined)
3409 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3412                                    progress and wait dialogs will ignore this property and always hide the
3413                                    close button as they can only be closed programmatically.
3414 cls               String           A custom CSS class to apply to the message box element
3415 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3416                                    displayed (defaults to 75)
3417 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3418                                    function will be btn (the name of the button that was clicked, if applicable,
3419                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3420                                    Progress and wait dialogs will ignore this option since they do not respond to
3421                                    user actions and can only be closed programmatically, so any required function
3422                                    should be called by the same code after it closes the dialog.
3423 icon              String           A CSS class that provides a background image to be used as an icon for
3424                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3426 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3427 modal             Boolean          False to allow user interaction with the page while the message box is
3428                                    displayed (defaults to true)
3429 msg               String           A string that will replace the existing message box body text (defaults
3430                                    to the XHTML-compliant non-breaking space character '&#160;')
3431 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3432 progress          Boolean          True to display a progress bar (defaults to false)
3433 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3436 title             String           The title text
3437 value             String           The string value to set into the active textbox element if displayed
3438 wait              Boolean          True to display a progress bar (defaults to false)
3439 width             Number           The width of the dialog in pixels
3440 </pre>
3441          *
3442          * Example usage:
3443          * <pre><code>
3444 Roo.Msg.show({
3445    title: 'Address',
3446    msg: 'Please enter your address:',
3447    width: 300,
3448    buttons: Roo.MessageBox.OKCANCEL,
3449    multiline: true,
3450    fn: saveAddress,
3451    animEl: 'addAddressBtn'
3452 });
3453 </code></pre>
3454          * @param {Object} config Configuration options
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         show : function(options)
3458         {
3459             
3460             // this causes nightmares if you show one dialog after another
3461             // especially on callbacks..
3462              
3463             if(this.isVisible()){
3464                 
3465                 this.hide();
3466                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3468                 Roo.log("New Dialog Message:" +  options.msg )
3469                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3471                 
3472             }
3473             var d = this.getDialog();
3474             opt = options;
3475             d.setTitle(opt.title || "&#160;");
3476             d.closeEl.setDisplayed(opt.closable !== false);
3477             activeTextEl = textboxEl;
3478             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3479             if(opt.prompt){
3480                 if(opt.multiline){
3481                     textboxEl.hide();
3482                     textareaEl.show();
3483                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3484                         opt.multiline : this.defaultTextHeight);
3485                     activeTextEl = textareaEl;
3486                 }else{
3487                     textboxEl.show();
3488                     textareaEl.hide();
3489                 }
3490             }else{
3491                 textboxEl.hide();
3492                 textareaEl.hide();
3493             }
3494             progressEl.setDisplayed(opt.progress === true);
3495             this.updateProgress(0);
3496             activeTextEl.dom.value = opt.value || "";
3497             if(opt.prompt){
3498                 dlg.setDefaultButton(activeTextEl);
3499             }else{
3500                 var bs = opt.buttons;
3501                 var db = null;
3502                 if(bs && bs.ok){
3503                     db = buttons["ok"];
3504                 }else if(bs && bs.yes){
3505                     db = buttons["yes"];
3506                 }
3507                 dlg.setDefaultButton(db);
3508             }
3509             bwidth = updateButtons(opt.buttons);
3510             this.updateText(opt.msg);
3511             if(opt.cls){
3512                 d.el.addClass(opt.cls);
3513             }
3514             d.proxyDrag = opt.proxyDrag === true;
3515             d.modal = opt.modal !== false;
3516             d.mask = opt.modal !== false ? mask : false;
3517             if(!d.isVisible()){
3518                 // force it to the end of the z-index stack so it gets a cursor in FF
3519                 document.body.appendChild(dlg.el.dom);
3520                 d.animateTarget = null;
3521                 d.show(options.animEl);
3522             }
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3528          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529          * and closing the message box when the process is complete.
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         progress : function(title, msg){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: false,
3539                 progress:true,
3540                 closable:false,
3541                 minWidth: this.minProgressWidth,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549          * If a callback function is passed it will be called after the user clicks the button, and the
3550          * id of the button that was clicked will be passed as the only parameter to the callback
3551          * (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         alert : function(title, msg, fn, scope)
3559         {
3560             this.show({
3561                 title : title,
3562                 msg : msg,
3563                 buttons: this.OK,
3564                 fn: fn,
3565                 closable : false,
3566                 scope : scope,
3567                 modal : true
3568             });
3569             return this;
3570         },
3571
3572         /**
3573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575          * You are responsible for closing the message box when the process is complete.
3576          * @param {String} msg The message box body text
3577          * @param {String} title (optional) The title bar text
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         wait : function(msg, title){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: false,
3585                 closable:false,
3586                 progress:true,
3587                 modal:true,
3588                 width:300,
3589                 wait:true
3590             });
3591             waitTimer = Roo.TaskMgr.start({
3592                 run: function(i){
3593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594                 },
3595                 interval: 1000
3596             });
3597             return this;
3598         },
3599
3600         /**
3601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604          * @param {String} title The title bar text
3605          * @param {String} msg The message box body text
3606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607          * @param {Object} scope (optional) The scope of the callback function
3608          * @return {Roo.MessageBox} This message box
3609          */
3610         confirm : function(title, msg, fn, scope){
3611             this.show({
3612                 title : title,
3613                 msg : msg,
3614                 buttons: this.YESNO,
3615                 fn: fn,
3616                 scope : scope,
3617                 modal : true
3618             });
3619             return this;
3620         },
3621
3622         /**
3623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626          * (could also be the top-right close button) and the text that was entered will be passed as the two
3627          * parameters to the callback.
3628          * @param {String} title The title bar text
3629          * @param {String} msg The message box body text
3630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631          * @param {Object} scope (optional) The scope of the callback function
3632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634          * @return {Roo.MessageBox} This message box
3635          */
3636         prompt : function(title, msg, fn, scope, multiline){
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OKCANCEL,
3641                 fn: fn,
3642                 minWidth:250,
3643                 scope : scope,
3644                 prompt:true,
3645                 multiline: multiline,
3646                 modal : true
3647             });
3648             return this;
3649         },
3650
3651         /**
3652          * Button config that displays a single OK button
3653          * @type Object
3654          */
3655         OK : {ok:true},
3656         /**
3657          * Button config that displays Yes and No buttons
3658          * @type Object
3659          */
3660         YESNO : {yes:true, no:true},
3661         /**
3662          * Button config that displays OK and Cancel buttons
3663          * @type Object
3664          */
3665         OKCANCEL : {ok:true, cancel:true},
3666         /**
3667          * Button config that displays Yes, No and Cancel buttons
3668          * @type Object
3669          */
3670         YESNOCANCEL : {yes:true, no:true, cancel:true},
3671
3672         /**
3673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3674          * @type Number
3675          */
3676         defaultTextHeight : 75,
3677         /**
3678          * The maximum width in pixels of the message box (defaults to 600)
3679          * @type Number
3680          */
3681         maxWidth : 600,
3682         /**
3683          * The minimum width in pixels of the message box (defaults to 100)
3684          * @type Number
3685          */
3686         minWidth : 100,
3687         /**
3688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3690          * @type Number
3691          */
3692         minProgressWidth : 250,
3693         /**
3694          * An object containing the default button text strings that can be overriden for localized language support.
3695          * Supported properties are: ok, cancel, yes and no.
3696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3697          * @type Object
3698          */
3699         buttonText : {
3700             ok : "OK",
3701             cancel : "Cancel",
3702             yes : "Yes",
3703             no : "No"
3704         }
3705     };
3706 }();
3707
3708 /**
3709  * Shorthand for {@link Roo.MessageBox}
3710  */
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3713 /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.Navbar
3722  * @extends Roo.bootstrap.Component
3723  * Bootstrap Navbar class
3724
3725  * @constructor
3726  * Create a new Navbar
3727  * @param {Object} config The config object
3728  */
3729
3730
3731 Roo.bootstrap.Navbar = function(config){
3732     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3733     this.addEvents({
3734         // raw events
3735         /**
3736          * @event beforetoggle
3737          * Fire before toggle the menu
3738          * @param {Roo.EventObject} e
3739          */
3740         "beforetoggle" : true
3741     });
3742 };
3743
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3745     
3746     
3747    
3748     // private
3749     navItems : false,
3750     loadMask : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3757         
3758     },
3759     
3760     initEvents :function ()
3761     {
3762         //Roo.log(this.el.select('.navbar-toggle',true));
3763         this.el.select('.navbar-toggle',true).on('click', function() {
3764             if(this.fireEvent('beforetoggle', this) !== false){
3765                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3766             }
3767             
3768         }, this);
3769         
3770         var mark = {
3771             tag: "div",
3772             cls:"x-dlg-mask"
3773         };
3774         
3775         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3776         
3777         var size = this.el.getSize();
3778         this.maskEl.setSize(size.width, size.height);
3779         this.maskEl.enableDisplayMode("block");
3780         this.maskEl.hide();
3781         
3782         if(this.loadMask){
3783             this.maskEl.show();
3784         }
3785     },
3786     
3787     
3788     getChildContainer : function()
3789     {
3790         if (this.el.select('.collapse').getCount()) {
3791             return this.el.select('.collapse',true).first();
3792         }
3793         
3794         return this.el;
3795     },
3796     
3797     mask : function()
3798     {
3799         this.maskEl.show();
3800     },
3801     
3802     unmask : function()
3803     {
3804         this.maskEl.hide();
3805     } 
3806     
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  /*
3817  * - LGPL
3818  *
3819  * navbar
3820  * 
3821  */
3822
3823 /**
3824  * @class Roo.bootstrap.NavSimplebar
3825  * @extends Roo.bootstrap.Navbar
3826  * Bootstrap Sidebar class
3827  *
3828  * @cfg {Boolean} inverse is inverted color
3829  * 
3830  * @cfg {String} type (nav | pills | tabs)
3831  * @cfg {Boolean} arrangement stacked | justified
3832  * @cfg {String} align (left | right) alignment
3833  * 
3834  * @cfg {Boolean} main (true|false) main nav bar? default false
3835  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3836  * 
3837  * @cfg {String} tag (header|footer|nav|div) default is nav 
3838
3839  * 
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new Sidebar
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.NavSimplebar = function(config){
3849     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3853     
3854     inverse: false,
3855     
3856     type: false,
3857     arrangement: '',
3858     align : false,
3859     
3860     
3861     
3862     main : false,
3863     
3864     
3865     tag : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         var cfg = {
3872             tag : this.tag || 'div',
3873             cls : 'navbar'
3874         };
3875           
3876         
3877         cfg.cn = [
3878             {
3879                 cls: 'nav',
3880                 tag : 'ul'
3881             }
3882         ];
3883         
3884          
3885         this.type = this.type || 'nav';
3886         if (['tabs','pills'].indexOf(this.type)!==-1) {
3887             cfg.cn[0].cls += ' nav-' + this.type
3888         
3889         
3890         } else {
3891             if (this.type!=='nav') {
3892                 Roo.log('nav type must be nav/tabs/pills')
3893             }
3894             cfg.cn[0].cls += ' navbar-nav'
3895         }
3896         
3897         
3898         
3899         
3900         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.arrangement;
3902         }
3903         
3904         
3905         if (this.align === 'right') {
3906             cfg.cn[0].cls += ' navbar-right';
3907         }
3908         
3909         if (this.inverse) {
3910             cfg.cls += ' navbar-inverse';
3911             
3912         }
3913         
3914         
3915         return cfg;
3916     
3917         
3918     }
3919     
3920     
3921     
3922 });
3923
3924
3925
3926  
3927
3928  
3929        /*
3930  * - LGPL
3931  *
3932  * navbar
3933  * 
3934  */
3935
3936 /**
3937  * @class Roo.bootstrap.NavHeaderbar
3938  * @extends Roo.bootstrap.NavSimplebar
3939  * Bootstrap Sidebar class
3940  *
3941  * @cfg {String} brand what is brand
3942  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943  * @cfg {String} brand_href href of the brand
3944  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3945  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3948  * 
3949  * @constructor
3950  * Create a new Sidebar
3951  * @param {Object} config The config object
3952  */
3953
3954
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3957       
3958 };
3959
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3961     
3962     position: '',
3963     brand: '',
3964     brand_href: false,
3965     srButton : true,
3966     autohide : false,
3967     desktopCenter : false,
3968    
3969     
3970     getAutoCreate : function(){
3971         
3972         var   cfg = {
3973             tag: this.nav || 'nav',
3974             cls: 'navbar',
3975             role: 'navigation',
3976             cn: []
3977         };
3978         
3979         var cn = cfg.cn;
3980         if (this.desktopCenter) {
3981             cn.push({cls : 'container', cn : []});
3982             cn = cn[0].cn;
3983         }
3984         
3985         if(this.srButton){
3986             cn.push({
3987                 tag: 'div',
3988                 cls: 'navbar-header',
3989                 cn: [
3990                     {
3991                         tag: 'button',
3992                         type: 'button',
3993                         cls: 'navbar-toggle',
3994                         'data-toggle': 'collapse',
3995                         cn: [
3996                             {
3997                                 tag: 'span',
3998                                 cls: 'sr-only',
3999                                 html: 'Toggle navigation'
4000                             },
4001                             {
4002                                 tag: 'span',
4003                                 cls: 'icon-bar'
4004                             },
4005                             {
4006                                 tag: 'span',
4007                                 cls: 'icon-bar'
4008                             },
4009                             {
4010                                 tag: 'span',
4011                                 cls: 'icon-bar'
4012                             }
4013                         ]
4014                     }
4015                 ]
4016             });
4017         }
4018         
4019         cn.push({
4020             tag: 'div',
4021             cls: 'collapse navbar-collapse',
4022             cn : []
4023         });
4024         
4025         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4026         
4027         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028             cfg.cls += ' navbar-' + this.position;
4029             
4030             // tag can override this..
4031             
4032             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4033         }
4034         
4035         if (this.brand !== '') {
4036             cn[0].cn.push({
4037                 tag: 'a',
4038                 href: this.brand_href ? this.brand_href : '#',
4039                 cls: 'navbar-brand',
4040                 cn: [
4041                 this.brand
4042                 ]
4043             });
4044         }
4045         
4046         if(this.main){
4047             cfg.cls += ' main-nav';
4048         }
4049         
4050         
4051         return cfg;
4052
4053         
4054     },
4055     getHeaderChildContainer : function()
4056     {
4057         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058             return this.el.select('.navbar-header',true).first();
4059         }
4060         
4061         return this.getChildContainer();
4062     },
4063     
4064     
4065     initEvents : function()
4066     {
4067         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4068         
4069         if (this.autohide) {
4070             
4071             var prevScroll = 0;
4072             var ft = this.el;
4073             
4074             Roo.get(document).on('scroll',function(e) {
4075                 var ns = Roo.get(document).getScroll().top;
4076                 var os = prevScroll;
4077                 prevScroll = ns;
4078                 
4079                 if(ns > os){
4080                     ft.removeClass('slideDown');
4081                     ft.addClass('slideUp');
4082                     return;
4083                 }
4084                 ft.removeClass('slideUp');
4085                 ft.addClass('slideDown');
4086                  
4087               
4088           },this);
4089         }
4090     }    
4091     
4092 });
4093
4094
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * navbar
4102  * 
4103  */
4104
4105 /**
4106  * @class Roo.bootstrap.NavSidebar
4107  * @extends Roo.bootstrap.Navbar
4108  * Bootstrap Sidebar class
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavSidebar = function(config){
4117     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4118 };
4119
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4121     
4122     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4123     
4124     getAutoCreate : function(){
4125         
4126         
4127         return  {
4128             tag: 'div',
4129             cls: 'sidebar sidebar-nav'
4130         };
4131     
4132         
4133     }
4134     
4135     
4136     
4137 });
4138
4139
4140
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * nav group
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.NavGroup
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap NavGroup class
4154  * @cfg {String} align (left|right)
4155  * @cfg {Boolean} inverse
4156  * @cfg {String} type (nav|pills|tab) default nav
4157  * @cfg {String} navId - reference Id for navbar.
4158
4159  * 
4160  * @constructor
4161  * Create a new nav group
4162  * @param {Object} config The config object
4163  */
4164
4165 Roo.bootstrap.NavGroup = function(config){
4166     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4167     this.navItems = [];
4168    
4169     Roo.bootstrap.NavGroup.register(this);
4170      this.addEvents({
4171         /**
4172              * @event changed
4173              * Fires when the active item changes
4174              * @param {Roo.bootstrap.NavGroup} this
4175              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4177          */
4178         'changed': true
4179      });
4180     
4181 };
4182
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4184     
4185     align: '',
4186     inverse: false,
4187     form: false,
4188     type: 'nav',
4189     navId : '',
4190     // private
4191     
4192     navItems : false, 
4193     
4194     getAutoCreate : function()
4195     {
4196         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4197         
4198         cfg = {
4199             tag : 'ul',
4200             cls: 'nav' 
4201         };
4202         
4203         if (['tabs','pills'].indexOf(this.type)!==-1) {
4204             cfg.cls += ' nav-' + this.type
4205         } else {
4206             if (this.type!=='nav') {
4207                 Roo.log('nav type must be nav/tabs/pills')
4208             }
4209             cfg.cls += ' navbar-nav'
4210         }
4211         
4212         if (this.parent() && this.parent().sidebar) {
4213             cfg = {
4214                 tag: 'ul',
4215                 cls: 'dashboard-menu sidebar-menu'
4216             };
4217             
4218             return cfg;
4219         }
4220         
4221         if (this.form === true) {
4222             cfg = {
4223                 tag: 'form',
4224                 cls: 'navbar-form'
4225             };
4226             
4227             if (this.align === 'right') {
4228                 cfg.cls += ' navbar-right';
4229             } else {
4230                 cfg.cls += ' navbar-left';
4231             }
4232         }
4233         
4234         if (this.align === 'right') {
4235             cfg.cls += ' navbar-right';
4236         }
4237         
4238         if (this.inverse) {
4239             cfg.cls += ' navbar-inverse';
4240             
4241         }
4242         
4243         
4244         return cfg;
4245     },
4246     /**
4247     * sets the active Navigation item
4248     * @param {Roo.bootstrap.NavItem} the new current navitem
4249     */
4250     setActiveItem : function(item)
4251     {
4252         var prev = false;
4253         Roo.each(this.navItems, function(v){
4254             if (v == item) {
4255                 return ;
4256             }
4257             if (v.isActive()) {
4258                 v.setActive(false, true);
4259                 prev = v;
4260                 
4261             }
4262             
4263         });
4264
4265         item.setActive(true, true);
4266         this.fireEvent('changed', this, item, prev);
4267         
4268         
4269     },
4270     /**
4271     * gets the active Navigation item
4272     * @return {Roo.bootstrap.NavItem} the current navitem
4273     */
4274     getActive : function()
4275     {
4276         
4277         var prev = false;
4278         Roo.each(this.navItems, function(v){
4279             
4280             if (v.isActive()) {
4281                 prev = v;
4282                 
4283             }
4284             
4285         });
4286         return prev;
4287     },
4288     
4289     indexOfNav : function()
4290     {
4291         
4292         var prev = false;
4293         Roo.each(this.navItems, function(v,i){
4294             
4295             if (v.isActive()) {
4296                 prev = i;
4297                 
4298             }
4299             
4300         });
4301         return prev;
4302     },
4303     /**
4304     * adds a Navigation item
4305     * @param {Roo.bootstrap.NavItem} the navitem to add
4306     */
4307     addItem : function(cfg)
4308     {
4309         var cn = new Roo.bootstrap.NavItem(cfg);
4310         this.register(cn);
4311         cn.parentId = this.id;
4312         cn.onRender(this.el, null);
4313         return cn;
4314     },
4315     /**
4316     * register a Navigation item
4317     * @param {Roo.bootstrap.NavItem} the navitem to add
4318     */
4319     register : function(item)
4320     {
4321         this.navItems.push( item);
4322         item.navId = this.navId;
4323     
4324     },
4325     
4326     /**
4327     * clear all the Navigation item
4328     */
4329    
4330     clearAll : function()
4331     {
4332         this.navItems = [];
4333         this.el.dom.innerHTML = '';
4334     },
4335     
4336     getNavItem: function(tabId)
4337     {
4338         var ret = false;
4339         Roo.each(this.navItems, function(e) {
4340             if (e.tabId == tabId) {
4341                ret =  e;
4342                return false;
4343             }
4344             return true;
4345             
4346         });
4347         return ret;
4348     },
4349     
4350     setActiveNext : function()
4351     {
4352         var i = this.indexOfNav(this.getActive());
4353         if (i > this.navItems.length) {
4354             return;
4355         }
4356         this.setActiveItem(this.navItems[i+1]);
4357     },
4358     setActivePrev : function()
4359     {
4360         var i = this.indexOfNav(this.getActive());
4361         if (i  < 1) {
4362             return;
4363         }
4364         this.setActiveItem(this.navItems[i-1]);
4365     },
4366     clearWasActive : function(except) {
4367         Roo.each(this.navItems, function(e) {
4368             if (e.tabId != except.tabId && e.was_active) {
4369                e.was_active = false;
4370                return false;
4371             }
4372             return true;
4373             
4374         });
4375     },
4376     getWasActive : function ()
4377     {
4378         var r = false;
4379         Roo.each(this.navItems, function(e) {
4380             if (e.was_active) {
4381                r = e;
4382                return false;
4383             }
4384             return true;
4385             
4386         });
4387         return r;
4388     }
4389     
4390     
4391 });
4392
4393  
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4395     
4396     groups: {},
4397      /**
4398     * register a Navigation Group
4399     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4400     */
4401     register : function(navgrp)
4402     {
4403         this.groups[navgrp.navId] = navgrp;
4404         
4405     },
4406     /**
4407     * fetch a Navigation Group based on the navigation ID
4408     * @param {string} the navgroup to add
4409     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4410     */
4411     get: function(navId) {
4412         if (typeof(this.groups[navId]) == 'undefined') {
4413             return false;
4414             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4415         }
4416         return this.groups[navId] ;
4417     }
4418     
4419     
4420     
4421 });
4422
4423  /*
4424  * - LGPL
4425  *
4426  * row
4427  * 
4428  */
4429
4430 /**
4431  * @class Roo.bootstrap.NavItem
4432  * @extends Roo.bootstrap.Component
4433  * Bootstrap Navbar.NavItem class
4434  * @cfg {String} href  link to
4435  * @cfg {String} html content of button
4436  * @cfg {String} badge text inside badge
4437  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438  * @cfg {String} glyphicon name of glyphicon
4439  * @cfg {String} icon name of font awesome icon
4440  * @cfg {Boolean} active Is item active
4441  * @cfg {Boolean} disabled Is item disabled
4442  
4443  * @cfg {Boolean} preventDefault (true | false) default false
4444  * @cfg {String} tabId the tab that this item activates.
4445  * @cfg {String} tagtype (a|span) render as a href or span?
4446  * @cfg {Boolean} animateRef (true|false) link to element default false  
4447   
4448  * @constructor
4449  * Create a new Navbar Item
4450  * @param {Object} config The config object
4451  */
4452 Roo.bootstrap.NavItem = function(config){
4453     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4454     this.addEvents({
4455         // raw events
4456         /**
4457          * @event click
4458          * The raw click event for the entire grid.
4459          * @param {Roo.EventObject} e
4460          */
4461         "click" : true,
4462          /**
4463             * @event changed
4464             * Fires when the active item active state changes
4465             * @param {Roo.bootstrap.NavItem} this
4466             * @param {boolean} state the new state
4467              
4468          */
4469         'changed': true,
4470         /**
4471             * @event scrollto
4472             * Fires when scroll to element
4473             * @param {Roo.bootstrap.NavItem} this
4474             * @param {Object} options
4475             * @param {Roo.EventObject} e
4476              
4477          */
4478         'scrollto': true
4479     });
4480    
4481 };
4482
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4484     
4485     href: false,
4486     html: '',
4487     badge: '',
4488     icon: false,
4489     glyphicon: false,
4490     active: false,
4491     preventDefault : false,
4492     tabId : false,
4493     tagtype : 'a',
4494     disabled : false,
4495     animateRef : false,
4496     was_active : false,
4497     
4498     getAutoCreate : function(){
4499          
4500         var cfg = {
4501             tag: 'li',
4502             cls: 'nav-item'
4503             
4504         };
4505         
4506         if (this.active) {
4507             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4508         }
4509         if (this.disabled) {
4510             cfg.cls += ' disabled';
4511         }
4512         
4513         if (this.href || this.html || this.glyphicon || this.icon) {
4514             cfg.cn = [
4515                 {
4516                     tag: this.tagtype,
4517                     href : this.href || "#",
4518                     html: this.html || ''
4519                 }
4520             ];
4521             
4522             if (this.icon) {
4523                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4524             }
4525
4526             if(this.glyphicon) {
4527                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4528             }
4529             
4530             if (this.menu) {
4531                 
4532                 cfg.cn[0].html += " <span class='caret'></span>";
4533              
4534             }
4535             
4536             if (this.badge !== '') {
4537                  
4538                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4539             }
4540         }
4541         
4542         
4543         
4544         return cfg;
4545     },
4546     initEvents: function() 
4547     {
4548         if (typeof (this.menu) != 'undefined') {
4549             this.menu.parentType = this.xtype;
4550             this.menu.triggerEl = this.el;
4551             this.menu = this.addxtype(Roo.apply({}, this.menu));
4552         }
4553         
4554         this.el.select('a',true).on('click', this.onClick, this);
4555         
4556         if(this.tagtype == 'span'){
4557             this.el.select('span',true).on('click', this.onClick, this);
4558         }
4559        
4560         // at this point parent should be available..
4561         this.parent().register(this);
4562     },
4563     
4564     onClick : function(e)
4565     {
4566         if (e.getTarget('.dropdown-menu-item')) {
4567             // did you click on a menu itemm.... - then don't trigger onclick..
4568             return;
4569         }
4570         
4571         if(
4572                 this.preventDefault || 
4573                 this.href == '#' 
4574         ){
4575             Roo.log("NavItem - prevent Default?");
4576             e.preventDefault();
4577         }
4578         
4579         if (this.disabled) {
4580             return;
4581         }
4582         
4583         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584         if (tg && tg.transition) {
4585             Roo.log("waiting for the transitionend");
4586             return;
4587         }
4588         
4589         
4590         
4591         //Roo.log("fire event clicked");
4592         if(this.fireEvent('click', this, e) === false){
4593             return;
4594         };
4595         
4596         if(this.tagtype == 'span'){
4597             return;
4598         }
4599         
4600         //Roo.log(this.href);
4601         var ael = this.el.select('a',true).first();
4602         //Roo.log(ael);
4603         
4604         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607                 return; // ignore... - it's a 'hash' to another page.
4608             }
4609             Roo.log("NavItem - prevent Default?");
4610             e.preventDefault();
4611             this.scrollToElement(e);
4612         }
4613         
4614         
4615         var p =  this.parent();
4616    
4617         if (['tabs','pills'].indexOf(p.type)!==-1) {
4618             if (typeof(p.setActiveItem) !== 'undefined') {
4619                 p.setActiveItem(this);
4620             }
4621         }
4622         
4623         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625             // remove the collapsed menu expand...
4626             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4627         }
4628     },
4629     
4630     isActive: function () {
4631         return this.active
4632     },
4633     setActive : function(state, fire, is_was_active)
4634     {
4635         if (this.active && !state && this.navId) {
4636             this.was_active = true;
4637             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4638             if (nv) {
4639                 nv.clearWasActive(this);
4640             }
4641             
4642         }
4643         this.active = state;
4644         
4645         if (!state ) {
4646             this.el.removeClass('active');
4647         } else if (!this.el.hasClass('active')) {
4648             this.el.addClass('active');
4649         }
4650         if (fire) {
4651             this.fireEvent('changed', this, state);
4652         }
4653         
4654         // show a panel if it's registered and related..
4655         
4656         if (!this.navId || !this.tabId || !state || is_was_active) {
4657             return;
4658         }
4659         
4660         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4661         if (!tg) {
4662             return;
4663         }
4664         var pan = tg.getPanelByName(this.tabId);
4665         if (!pan) {
4666             return;
4667         }
4668         // if we can not flip to new panel - go back to old nav highlight..
4669         if (false == tg.showPanel(pan)) {
4670             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4671             if (nv) {
4672                 var onav = nv.getWasActive();
4673                 if (onav) {
4674                     onav.setActive(true, false, true);
4675                 }
4676             }
4677             
4678         }
4679         
4680         
4681         
4682     },
4683      // this should not be here...
4684     setDisabled : function(state)
4685     {
4686         this.disabled = state;
4687         if (!state ) {
4688             this.el.removeClass('disabled');
4689         } else if (!this.el.hasClass('disabled')) {
4690             this.el.addClass('disabled');
4691         }
4692         
4693     },
4694     
4695     /**
4696      * Fetch the element to display the tooltip on.
4697      * @return {Roo.Element} defaults to this.el
4698      */
4699     tooltipEl : function()
4700     {
4701         return this.el.select('' + this.tagtype + '', true).first();
4702     },
4703     
4704     scrollToElement : function(e)
4705     {
4706         var c = document.body;
4707         
4708         /*
4709          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4710          */
4711         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712             c = document.documentElement;
4713         }
4714         
4715         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4716         
4717         if(!target){
4718             return;
4719         }
4720
4721         var o = target.calcOffsetsTo(c);
4722         
4723         var options = {
4724             target : target,
4725             value : o[1]
4726         };
4727         
4728         this.fireEvent('scrollto', this, options, e);
4729         
4730         Roo.get(c).scrollTo('top', options.value, true);
4731         
4732         return;
4733     }
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * sidebar item
4741  *
4742  *  li
4743  *    <span> icon </span>
4744  *    <span> text </span>
4745  *    <span>badge </span>
4746  */
4747
4748 /**
4749  * @class Roo.bootstrap.NavSidebarItem
4750  * @extends Roo.bootstrap.NavItem
4751  * Bootstrap Navbar.NavSidebarItem class
4752  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753  * {Boolean} open is the menu open
4754  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756  * {String} buttonSize (sm|md|lg)the extra classes for the button
4757  * {Boolean} showArrow show arrow next to the text (default true)
4758  * @constructor
4759  * Create a new Navbar Button
4760  * @param {Object} config The config object
4761  */
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4764     this.addEvents({
4765         // raw events
4766         /**
4767          * @event click
4768          * The raw click event for the entire grid.
4769          * @param {Roo.EventObject} e
4770          */
4771         "click" : true,
4772          /**
4773             * @event changed
4774             * Fires when the active item active state changes
4775             * @param {Roo.bootstrap.NavSidebarItem} this
4776             * @param {boolean} state the new state
4777              
4778          */
4779         'changed': true
4780     });
4781    
4782 };
4783
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4785     
4786     badgeWeight : 'default',
4787     
4788     open: false,
4789     
4790     buttonView : false,
4791     
4792     buttonWeight : 'default',
4793     
4794     buttonSize : 'md',
4795     
4796     showArrow : true,
4797     
4798     getAutoCreate : function(){
4799         
4800         
4801         var a = {
4802                 tag: 'a',
4803                 href : this.href || '#',
4804                 cls: '',
4805                 html : '',
4806                 cn : []
4807         };
4808         
4809         if(this.buttonView){
4810             a = {
4811                 tag: 'button',
4812                 href : this.href || '#',
4813                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4814                 html : this.html,
4815                 cn : []
4816             };
4817         }
4818         
4819         var cfg = {
4820             tag: 'li',
4821             cls: '',
4822             cn: [ a ]
4823         };
4824         
4825         if (this.active) {
4826             cfg.cls += ' active';
4827         }
4828         
4829         if (this.disabled) {
4830             cfg.cls += ' disabled';
4831         }
4832         if (this.open) {
4833             cfg.cls += ' open x-open';
4834         }
4835         // left icon..
4836         if (this.glyphicon || this.icon) {
4837             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4838             a.cn.push({ tag : 'i', cls : c }) ;
4839         }
4840         
4841         if(!this.buttonView){
4842             var span = {
4843                 tag: 'span',
4844                 html : this.html || ''
4845             };
4846
4847             a.cn.push(span);
4848             
4849         }
4850         
4851         if (this.badge !== '') {
4852             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4853         }
4854         
4855         if (this.menu) {
4856             
4857             if(this.showArrow){
4858                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4859             }
4860             
4861             a.cls += ' dropdown-toggle treeview' ;
4862         }
4863         
4864         return cfg;
4865     },
4866     
4867     initEvents : function()
4868     { 
4869         if (typeof (this.menu) != 'undefined') {
4870             this.menu.parentType = this.xtype;
4871             this.menu.triggerEl = this.el;
4872             this.menu = this.addxtype(Roo.apply({}, this.menu));
4873         }
4874         
4875         this.el.on('click', this.onClick, this);
4876         
4877         if(this.badge !== ''){
4878             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4879         }
4880         
4881     },
4882     
4883     onClick : function(e)
4884     {
4885         if(this.disabled){
4886             e.preventDefault();
4887             return;
4888         }
4889         
4890         if(this.preventDefault){
4891             e.preventDefault();
4892         }
4893         
4894         this.fireEvent('click', this);
4895     },
4896     
4897     disable : function()
4898     {
4899         this.setDisabled(true);
4900     },
4901     
4902     enable : function()
4903     {
4904         this.setDisabled(false);
4905     },
4906     
4907     setDisabled : function(state)
4908     {
4909         if(this.disabled == state){
4910             return;
4911         }
4912         
4913         this.disabled = state;
4914         
4915         if (state) {
4916             this.el.addClass('disabled');
4917             return;
4918         }
4919         
4920         this.el.removeClass('disabled');
4921         
4922         return;
4923     },
4924     
4925     setActive : function(state)
4926     {
4927         if(this.active == state){
4928             return;
4929         }
4930         
4931         this.active = state;
4932         
4933         if (state) {
4934             this.el.addClass('active');
4935             return;
4936         }
4937         
4938         this.el.removeClass('active');
4939         
4940         return;
4941     },
4942     
4943     isActive: function () 
4944     {
4945         return this.active;
4946     },
4947     
4948     setBadge : function(str)
4949     {
4950         if(!this.badgeEl){
4951             return;
4952         }
4953         
4954         this.badgeEl.dom.innerHTML = str;
4955     }
4956     
4957    
4958      
4959  
4960 });
4961  
4962
4963  /*
4964  * - LGPL
4965  *
4966  * row
4967  * 
4968  */
4969
4970 /**
4971  * @class Roo.bootstrap.Row
4972  * @extends Roo.bootstrap.Component
4973  * Bootstrap Row class (contains columns...)
4974  * 
4975  * @constructor
4976  * Create a new Row
4977  * @param {Object} config The config object
4978  */
4979
4980 Roo.bootstrap.Row = function(config){
4981     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4982 };
4983
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4985     
4986     getAutoCreate : function(){
4987        return {
4988             cls: 'row clearfix'
4989        };
4990     }
4991     
4992     
4993 });
4994
4995  
4996
4997  /*
4998  * - LGPL
4999  *
5000  * element
5001  * 
5002  */
5003
5004 /**
5005  * @class Roo.bootstrap.Element
5006  * @extends Roo.bootstrap.Component
5007  * Bootstrap Element class
5008  * @cfg {String} html contents of the element
5009  * @cfg {String} tag tag of the element
5010  * @cfg {String} cls class of the element
5011  * @cfg {Boolean} preventDefault (true|false) default false
5012  * @cfg {Boolean} clickable (true|false) default false
5013  * 
5014  * @constructor
5015  * Create a new Element
5016  * @param {Object} config The config object
5017  */
5018
5019 Roo.bootstrap.Element = function(config){
5020     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5021     
5022     this.addEvents({
5023         // raw events
5024         /**
5025          * @event click
5026          * When a element is chick
5027          * @param {Roo.bootstrap.Element} this
5028          * @param {Roo.EventObject} e
5029          */
5030         "click" : true
5031     });
5032 };
5033
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5035     
5036     tag: 'div',
5037     cls: '',
5038     html: '',
5039     preventDefault: false, 
5040     clickable: false,
5041     
5042     getAutoCreate : function(){
5043         
5044         var cfg = {
5045             tag: this.tag,
5046             // cls: this.cls, double assign in parent class Component.js :: onRender
5047             html: this.html
5048         };
5049         
5050         return cfg;
5051     },
5052     
5053     initEvents: function() 
5054     {
5055         Roo.bootstrap.Element.superclass.initEvents.call(this);
5056         
5057         if(this.clickable){
5058             this.el.on('click', this.onClick, this);
5059         }
5060         
5061     },
5062     
5063     onClick : function(e)
5064     {
5065         if(this.preventDefault){
5066             e.preventDefault();
5067         }
5068         
5069         this.fireEvent('click', this, e);
5070     },
5071     
5072     getValue : function()
5073     {
5074         return this.el.dom.innerHTML;
5075     },
5076     
5077     setValue : function(value)
5078     {
5079         this.el.dom.innerHTML = value;
5080     }
5081    
5082 });
5083
5084  
5085
5086  /*
5087  * - LGPL
5088  *
5089  * pagination
5090  * 
5091  */
5092
5093 /**
5094  * @class Roo.bootstrap.Pagination
5095  * @extends Roo.bootstrap.Component
5096  * Bootstrap Pagination class
5097  * @cfg {String} size xs | sm | md | lg
5098  * @cfg {Boolean} inverse false | true
5099  * 
5100  * @constructor
5101  * Create a new Pagination
5102  * @param {Object} config The config object
5103  */
5104
5105 Roo.bootstrap.Pagination = function(config){
5106     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5107 };
5108
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5110     
5111     cls: false,
5112     size: false,
5113     inverse: false,
5114     
5115     getAutoCreate : function(){
5116         var cfg = {
5117             tag: 'ul',
5118                 cls: 'pagination'
5119         };
5120         if (this.inverse) {
5121             cfg.cls += ' inverse';
5122         }
5123         if (this.html) {
5124             cfg.html=this.html;
5125         }
5126         if (this.cls) {
5127             cfg.cls += " " + this.cls;
5128         }
5129         return cfg;
5130     }
5131    
5132 });
5133
5134  
5135
5136  /*
5137  * - LGPL
5138  *
5139  * Pagination item
5140  * 
5141  */
5142
5143
5144 /**
5145  * @class Roo.bootstrap.PaginationItem
5146  * @extends Roo.bootstrap.Component
5147  * Bootstrap PaginationItem class
5148  * @cfg {String} html text
5149  * @cfg {String} href the link
5150  * @cfg {Boolean} preventDefault (true | false) default true
5151  * @cfg {Boolean} active (true | false) default false
5152  * @cfg {Boolean} disabled default false
5153  * 
5154  * 
5155  * @constructor
5156  * Create a new PaginationItem
5157  * @param {Object} config The config object
5158  */
5159
5160
5161 Roo.bootstrap.PaginationItem = function(config){
5162     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5163     this.addEvents({
5164         // raw events
5165         /**
5166          * @event click
5167          * The raw click event for the entire grid.
5168          * @param {Roo.EventObject} e
5169          */
5170         "click" : true
5171     });
5172 };
5173
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5175     
5176     href : false,
5177     html : false,
5178     preventDefault: true,
5179     active : false,
5180     cls : false,
5181     disabled: false,
5182     
5183     getAutoCreate : function(){
5184         var cfg= {
5185             tag: 'li',
5186             cn: [
5187                 {
5188                     tag : 'a',
5189                     href : this.href ? this.href : '#',
5190                     html : this.html ? this.html : ''
5191                 }
5192             ]
5193         };
5194         
5195         if(this.cls){
5196             cfg.cls = this.cls;
5197         }
5198         
5199         if(this.disabled){
5200             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5201         }
5202         
5203         if(this.active){
5204             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5205         }
5206         
5207         return cfg;
5208     },
5209     
5210     initEvents: function() {
5211         
5212         this.el.on('click', this.onClick, this);
5213         
5214     },
5215     onClick : function(e)
5216     {
5217         Roo.log('PaginationItem on click ');
5218         if(this.preventDefault){
5219             e.preventDefault();
5220         }
5221         
5222         if(this.disabled){
5223             return;
5224         }
5225         
5226         this.fireEvent('click', this, e);
5227     }
5228    
5229 });
5230
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * slider
5237  * 
5238  */
5239
5240
5241 /**
5242  * @class Roo.bootstrap.Slider
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Slider class
5245  *    
5246  * @constructor
5247  * Create a new Slider
5248  * @param {Object} config The config object
5249  */
5250
5251 Roo.bootstrap.Slider = function(config){
5252     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5253 };
5254
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5256     
5257     getAutoCreate : function(){
5258         
5259         var cfg = {
5260             tag: 'div',
5261             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5262             cn: [
5263                 {
5264                     tag: 'a',
5265                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5266                 }
5267             ]
5268         };
5269         
5270         return cfg;
5271     }
5272    
5273 });
5274
5275  /*
5276  * Based on:
5277  * Ext JS Library 1.1.1
5278  * Copyright(c) 2006-2007, Ext JS, LLC.
5279  *
5280  * Originally Released Under LGPL - original licence link has changed is not relivant.
5281  *
5282  * Fork - LGPL
5283  * <script type="text/javascript">
5284  */
5285  
5286
5287 /**
5288  * @class Roo.grid.ColumnModel
5289  * @extends Roo.util.Observable
5290  * This is the default implementation of a ColumnModel used by the Grid. It defines
5291  * the columns in the grid.
5292  * <br>Usage:<br>
5293  <pre><code>
5294  var colModel = new Roo.grid.ColumnModel([
5295         {header: "Ticker", width: 60, sortable: true, locked: true},
5296         {header: "Company Name", width: 150, sortable: true},
5297         {header: "Market Cap.", width: 100, sortable: true},
5298         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299         {header: "Employees", width: 100, sortable: true, resizable: false}
5300  ]);
5301  </code></pre>
5302  * <p>
5303  
5304  * The config options listed for this class are options which may appear in each
5305  * individual column definition.
5306  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5307  * @constructor
5308  * @param {Object} config An Array of column config objects. See this class's
5309  * config objects for details.
5310 */
5311 Roo.grid.ColumnModel = function(config){
5312         /**
5313      * The config passed into the constructor
5314      */
5315     this.config = config;
5316     this.lookup = {};
5317
5318     // if no id, create one
5319     // if the column does not have a dataIndex mapping,
5320     // map it to the order it is in the config
5321     for(var i = 0, len = config.length; i < len; i++){
5322         var c = config[i];
5323         if(typeof c.dataIndex == "undefined"){
5324             c.dataIndex = i;
5325         }
5326         if(typeof c.renderer == "string"){
5327             c.renderer = Roo.util.Format[c.renderer];
5328         }
5329         if(typeof c.id == "undefined"){
5330             c.id = Roo.id();
5331         }
5332         if(c.editor && c.editor.xtype){
5333             c.editor  = Roo.factory(c.editor, Roo.grid);
5334         }
5335         if(c.editor && c.editor.isFormField){
5336             c.editor = new Roo.grid.GridEditor(c.editor);
5337         }
5338         this.lookup[c.id] = c;
5339     }
5340
5341     /**
5342      * The width of columns which have no width specified (defaults to 100)
5343      * @type Number
5344      */
5345     this.defaultWidth = 100;
5346
5347     /**
5348      * Default sortable of columns which have no sortable specified (defaults to false)
5349      * @type Boolean
5350      */
5351     this.defaultSortable = false;
5352
5353     this.addEvents({
5354         /**
5355              * @event widthchange
5356              * Fires when the width of a column changes.
5357              * @param {ColumnModel} this
5358              * @param {Number} columnIndex The column index
5359              * @param {Number} newWidth The new width
5360              */
5361             "widthchange": true,
5362         /**
5363              * @event headerchange
5364              * Fires when the text of a header changes.
5365              * @param {ColumnModel} this
5366              * @param {Number} columnIndex The column index
5367              * @param {Number} newText The new header text
5368              */
5369             "headerchange": true,
5370         /**
5371              * @event hiddenchange
5372              * Fires when a column is hidden or "unhidden".
5373              * @param {ColumnModel} this
5374              * @param {Number} columnIndex The column index
5375              * @param {Boolean} hidden true if hidden, false otherwise
5376              */
5377             "hiddenchange": true,
5378             /**
5379          * @event columnmoved
5380          * Fires when a column is moved.
5381          * @param {ColumnModel} this
5382          * @param {Number} oldIndex
5383          * @param {Number} newIndex
5384          */
5385         "columnmoved" : true,
5386         /**
5387          * @event columlockchange
5388          * Fires when a column's locked state is changed
5389          * @param {ColumnModel} this
5390          * @param {Number} colIndex
5391          * @param {Boolean} locked true if locked
5392          */
5393         "columnlockchange" : true
5394     });
5395     Roo.grid.ColumnModel.superclass.constructor.call(this);
5396 };
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5398     /**
5399      * @cfg {String} header The header text to display in the Grid view.
5400      */
5401     /**
5402      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404      * specified, the column's index is used as an index into the Record's data Array.
5405      */
5406     /**
5407      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5409      */
5410     /**
5411      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412      * Defaults to the value of the {@link #defaultSortable} property.
5413      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5414      */
5415     /**
5416      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5417      */
5418     /**
5419      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5420      */
5421     /**
5422      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5423      */
5424     /**
5425      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5426      */
5427     /**
5428      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5432      */
5433        /**
5434      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5435      */
5436     /**
5437      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5438      */
5439     /**
5440      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5441      */
5442     /**
5443      * @cfg {String} cursor (Optional)
5444      */
5445     /**
5446      * @cfg {String} tooltip (Optional)
5447      */
5448     /**
5449      * @cfg {Number} xs (Optional)
5450      */
5451     /**
5452      * @cfg {Number} sm (Optional)
5453      */
5454     /**
5455      * @cfg {Number} md (Optional)
5456      */
5457     /**
5458      * @cfg {Number} lg (Optional)
5459      */
5460     /**
5461      * Returns the id of the column at the specified index.
5462      * @param {Number} index The column index
5463      * @return {String} the id
5464      */
5465     getColumnId : function(index){
5466         return this.config[index].id;
5467     },
5468
5469     /**
5470      * Returns the column for a specified id.
5471      * @param {String} id The column id
5472      * @return {Object} the column
5473      */
5474     getColumnById : function(id){
5475         return this.lookup[id];
5476     },
5477
5478     
5479     /**
5480      * Returns the column for a specified dataIndex.
5481      * @param {String} dataIndex The column dataIndex
5482      * @return {Object|Boolean} the column or false if not found
5483      */
5484     getColumnByDataIndex: function(dataIndex){
5485         var index = this.findColumnIndex(dataIndex);
5486         return index > -1 ? this.config[index] : false;
5487     },
5488     
5489     /**
5490      * Returns the index for a specified column id.
5491      * @param {String} id The column id
5492      * @return {Number} the index, or -1 if not found
5493      */
5494     getIndexById : function(id){
5495         for(var i = 0, len = this.config.length; i < len; i++){
5496             if(this.config[i].id == id){
5497                 return i;
5498             }
5499         }
5500         return -1;
5501     },
5502     
5503     /**
5504      * Returns the index for a specified column dataIndex.
5505      * @param {String} dataIndex The column dataIndex
5506      * @return {Number} the index, or -1 if not found
5507      */
5508     
5509     findColumnIndex : function(dataIndex){
5510         for(var i = 0, len = this.config.length; i < len; i++){
5511             if(this.config[i].dataIndex == dataIndex){
5512                 return i;
5513             }
5514         }
5515         return -1;
5516     },
5517     
5518     
5519     moveColumn : function(oldIndex, newIndex){
5520         var c = this.config[oldIndex];
5521         this.config.splice(oldIndex, 1);
5522         this.config.splice(newIndex, 0, c);
5523         this.dataMap = null;
5524         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5525     },
5526
5527     isLocked : function(colIndex){
5528         return this.config[colIndex].locked === true;
5529     },
5530
5531     setLocked : function(colIndex, value, suppressEvent){
5532         if(this.isLocked(colIndex) == value){
5533             return;
5534         }
5535         this.config[colIndex].locked = value;
5536         if(!suppressEvent){
5537             this.fireEvent("columnlockchange", this, colIndex, value);
5538         }
5539     },
5540
5541     getTotalLockedWidth : function(){
5542         var totalWidth = 0;
5543         for(var i = 0; i < this.config.length; i++){
5544             if(this.isLocked(i) && !this.isHidden(i)){
5545                 this.totalWidth += this.getColumnWidth(i);
5546             }
5547         }
5548         return totalWidth;
5549     },
5550
5551     getLockedCount : function(){
5552         for(var i = 0, len = this.config.length; i < len; i++){
5553             if(!this.isLocked(i)){
5554                 return i;
5555             }
5556         }
5557         
5558         return this.config.length;
5559     },
5560
5561     /**
5562      * Returns the number of columns.
5563      * @return {Number}
5564      */
5565     getColumnCount : function(visibleOnly){
5566         if(visibleOnly === true){
5567             var c = 0;
5568             for(var i = 0, len = this.config.length; i < len; i++){
5569                 if(!this.isHidden(i)){
5570                     c++;
5571                 }
5572             }
5573             return c;
5574         }
5575         return this.config.length;
5576     },
5577
5578     /**
5579      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580      * @param {Function} fn
5581      * @param {Object} scope (optional)
5582      * @return {Array} result
5583      */
5584     getColumnsBy : function(fn, scope){
5585         var r = [];
5586         for(var i = 0, len = this.config.length; i < len; i++){
5587             var c = this.config[i];
5588             if(fn.call(scope||this, c, i) === true){
5589                 r[r.length] = c;
5590             }
5591         }
5592         return r;
5593     },
5594
5595     /**
5596      * Returns true if the specified column is sortable.
5597      * @param {Number} col The column index
5598      * @return {Boolean}
5599      */
5600     isSortable : function(col){
5601         if(typeof this.config[col].sortable == "undefined"){
5602             return this.defaultSortable;
5603         }
5604         return this.config[col].sortable;
5605     },
5606
5607     /**
5608      * Returns the rendering (formatting) function defined for the column.
5609      * @param {Number} col The column index.
5610      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5611      */
5612     getRenderer : function(col){
5613         if(!this.config[col].renderer){
5614             return Roo.grid.ColumnModel.defaultRenderer;
5615         }
5616         return this.config[col].renderer;
5617     },
5618
5619     /**
5620      * Sets the rendering (formatting) function for a column.
5621      * @param {Number} col The column index
5622      * @param {Function} fn The function to use to process the cell's raw data
5623      * to return HTML markup for the grid view. The render function is called with
5624      * the following parameters:<ul>
5625      * <li>Data value.</li>
5626      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627      * <li>css A CSS style string to apply to the table cell.</li>
5628      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630      * <li>Row index</li>
5631      * <li>Column index</li>
5632      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5633      */
5634     setRenderer : function(col, fn){
5635         this.config[col].renderer = fn;
5636     },
5637
5638     /**
5639      * Returns the width for the specified column.
5640      * @param {Number} col The column index
5641      * @return {Number}
5642      */
5643     getColumnWidth : function(col){
5644         return this.config[col].width * 1 || this.defaultWidth;
5645     },
5646
5647     /**
5648      * Sets the width for a column.
5649      * @param {Number} col The column index
5650      * @param {Number} width The new width
5651      */
5652     setColumnWidth : function(col, width, suppressEvent){
5653         this.config[col].width = width;
5654         this.totalWidth = null;
5655         if(!suppressEvent){
5656              this.fireEvent("widthchange", this, col, width);
5657         }
5658     },
5659
5660     /**
5661      * Returns the total width of all columns.
5662      * @param {Boolean} includeHidden True to include hidden column widths
5663      * @return {Number}
5664      */
5665     getTotalWidth : function(includeHidden){
5666         if(!this.totalWidth){
5667             this.totalWidth = 0;
5668             for(var i = 0, len = this.config.length; i < len; i++){
5669                 if(includeHidden || !this.isHidden(i)){
5670                     this.totalWidth += this.getColumnWidth(i);
5671                 }
5672             }
5673         }
5674         return this.totalWidth;
5675     },
5676
5677     /**
5678      * Returns the header for the specified column.
5679      * @param {Number} col The column index
5680      * @return {String}
5681      */
5682     getColumnHeader : function(col){
5683         return this.config[col].header;
5684     },
5685
5686     /**
5687      * Sets the header for a column.
5688      * @param {Number} col The column index
5689      * @param {String} header The new header
5690      */
5691     setColumnHeader : function(col, header){
5692         this.config[col].header = header;
5693         this.fireEvent("headerchange", this, col, header);
5694     },
5695
5696     /**
5697      * Returns the tooltip for the specified column.
5698      * @param {Number} col The column index
5699      * @return {String}
5700      */
5701     getColumnTooltip : function(col){
5702             return this.config[col].tooltip;
5703     },
5704     /**
5705      * Sets the tooltip for a column.
5706      * @param {Number} col The column index
5707      * @param {String} tooltip The new tooltip
5708      */
5709     setColumnTooltip : function(col, tooltip){
5710             this.config[col].tooltip = tooltip;
5711     },
5712
5713     /**
5714      * Returns the dataIndex for the specified column.
5715      * @param {Number} col The column index
5716      * @return {Number}
5717      */
5718     getDataIndex : function(col){
5719         return this.config[col].dataIndex;
5720     },
5721
5722     /**
5723      * Sets the dataIndex for a column.
5724      * @param {Number} col The column index
5725      * @param {Number} dataIndex The new dataIndex
5726      */
5727     setDataIndex : function(col, dataIndex){
5728         this.config[col].dataIndex = dataIndex;
5729     },
5730
5731     
5732     
5733     /**
5734      * Returns true if the cell is editable.
5735      * @param {Number} colIndex The column index
5736      * @param {Number} rowIndex The row index - this is nto actually used..?
5737      * @return {Boolean}
5738      */
5739     isCellEditable : function(colIndex, rowIndex){
5740         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5741     },
5742
5743     /**
5744      * Returns the editor defined for the cell/column.
5745      * return false or null to disable editing.
5746      * @param {Number} colIndex The column index
5747      * @param {Number} rowIndex The row index
5748      * @return {Object}
5749      */
5750     getCellEditor : function(colIndex, rowIndex){
5751         return this.config[colIndex].editor;
5752     },
5753
5754     /**
5755      * Sets if a column is editable.
5756      * @param {Number} col The column index
5757      * @param {Boolean} editable True if the column is editable
5758      */
5759     setEditable : function(col, editable){
5760         this.config[col].editable = editable;
5761     },
5762
5763
5764     /**
5765      * Returns true if the column is hidden.
5766      * @param {Number} colIndex The column index
5767      * @return {Boolean}
5768      */
5769     isHidden : function(colIndex){
5770         return this.config[colIndex].hidden;
5771     },
5772
5773
5774     /**
5775      * Returns true if the column width cannot be changed
5776      */
5777     isFixed : function(colIndex){
5778         return this.config[colIndex].fixed;
5779     },
5780
5781     /**
5782      * Returns true if the column can be resized
5783      * @return {Boolean}
5784      */
5785     isResizable : function(colIndex){
5786         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5787     },
5788     /**
5789      * Sets if a column is hidden.
5790      * @param {Number} colIndex The column index
5791      * @param {Boolean} hidden True if the column is hidden
5792      */
5793     setHidden : function(colIndex, hidden){
5794         this.config[colIndex].hidden = hidden;
5795         this.totalWidth = null;
5796         this.fireEvent("hiddenchange", this, colIndex, hidden);
5797     },
5798
5799     /**
5800      * Sets the editor for a column.
5801      * @param {Number} col The column index
5802      * @param {Object} editor The editor object
5803      */
5804     setEditor : function(col, editor){
5805         this.config[col].editor = editor;
5806     }
5807 });
5808
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5810 {
5811     if(typeof value == "object") {
5812         return value;
5813     }
5814         if(typeof value == "string" && value.length < 1){
5815             return "&#160;";
5816         }
5817     
5818         return String.format("{0}", value);
5819 };
5820
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5823 /*
5824  * Based on:
5825  * Ext JS Library 1.1.1
5826  * Copyright(c) 2006-2007, Ext JS, LLC.
5827  *
5828  * Originally Released Under LGPL - original licence link has changed is not relivant.
5829  *
5830  * Fork - LGPL
5831  * <script type="text/javascript">
5832  */
5833  
5834 /**
5835  * @class Roo.LoadMask
5836  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5837  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5839  * element's UpdateManager load indicator and will be destroyed after the initial load.
5840  * @constructor
5841  * Create a new LoadMask
5842  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843  * @param {Object} config The config object
5844  */
5845 Roo.LoadMask = function(el, config){
5846     this.el = Roo.get(el);
5847     Roo.apply(this, config);
5848     if(this.store){
5849         this.store.on('beforeload', this.onBeforeLoad, this);
5850         this.store.on('load', this.onLoad, this);
5851         this.store.on('loadexception', this.onLoadException, this);
5852         this.removeMask = false;
5853     }else{
5854         var um = this.el.getUpdateManager();
5855         um.showLoadIndicator = false; // disable the default indicator
5856         um.on('beforeupdate', this.onBeforeLoad, this);
5857         um.on('update', this.onLoad, this);
5858         um.on('failure', this.onLoad, this);
5859         this.removeMask = true;
5860     }
5861 };
5862
5863 Roo.LoadMask.prototype = {
5864     /**
5865      * @cfg {Boolean} removeMask
5866      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5868      */
5869     /**
5870      * @cfg {String} msg
5871      * The text to display in a centered loading message box (defaults to 'Loading...')
5872      */
5873     msg : 'Loading...',
5874     /**
5875      * @cfg {String} msgCls
5876      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5877      */
5878     msgCls : 'x-mask-loading',
5879
5880     /**
5881      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5882      * @type Boolean
5883      */
5884     disabled: false,
5885
5886     /**
5887      * Disables the mask to prevent it from being displayed
5888      */
5889     disable : function(){
5890        this.disabled = true;
5891     },
5892
5893     /**
5894      * Enables the mask so that it can be displayed
5895      */
5896     enable : function(){
5897         this.disabled = false;
5898     },
5899     
5900     onLoadException : function()
5901     {
5902         Roo.log(arguments);
5903         
5904         if (typeof(arguments[3]) != 'undefined') {
5905             Roo.MessageBox.alert("Error loading",arguments[3]);
5906         } 
5907         /*
5908         try {
5909             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5911             }   
5912         } catch(e) {
5913             
5914         }
5915         */
5916     
5917         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5918     },
5919     // private
5920     onLoad : function()
5921     {
5922         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923     },
5924
5925     // private
5926     onBeforeLoad : function(){
5927         if(!this.disabled){
5928             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5929         }
5930     },
5931
5932     // private
5933     destroy : function(){
5934         if(this.store){
5935             this.store.un('beforeload', this.onBeforeLoad, this);
5936             this.store.un('load', this.onLoad, this);
5937             this.store.un('loadexception', this.onLoadException, this);
5938         }else{
5939             var um = this.el.getUpdateManager();
5940             um.un('beforeupdate', this.onBeforeLoad, this);
5941             um.un('update', this.onLoad, this);
5942             um.un('failure', this.onLoad, this);
5943         }
5944     }
5945 };/*
5946  * - LGPL
5947  *
5948  * table
5949  * 
5950  */
5951
5952 /**
5953  * @class Roo.bootstrap.Table
5954  * @extends Roo.bootstrap.Component
5955  * Bootstrap Table class
5956  * @cfg {String} cls table class
5957  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958  * @cfg {String} bgcolor Specifies the background color for a table
5959  * @cfg {Number} border Specifies whether the table cells should have borders or not
5960  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961  * @cfg {Number} cellspacing Specifies the space between cells
5962  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964  * @cfg {String} sortable Specifies that the table should be sortable
5965  * @cfg {String} summary Specifies a summary of the content of a table
5966  * @cfg {Number} width Specifies the width of a table
5967  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5968  * 
5969  * @cfg {boolean} striped Should the rows be alternative striped
5970  * @cfg {boolean} bordered Add borders to the table
5971  * @cfg {boolean} hover Add hover highlighting
5972  * @cfg {boolean} condensed Format condensed
5973  * @cfg {boolean} responsive Format condensed
5974  * @cfg {Boolean} loadMask (true|false) default false
5975  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977  * @cfg {Boolean} rowSelection (true|false) default false
5978  * @cfg {Boolean} cellSelection (true|false) default false
5979  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5981  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5982  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5983  
5984  * 
5985  * @constructor
5986  * Create a new Table
5987  * @param {Object} config The config object
5988  */
5989
5990 Roo.bootstrap.Table = function(config){
5991     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5992     
5993   
5994     
5995     // BC...
5996     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6000     
6001     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6002     if (this.sm) {
6003         this.sm.grid = this;
6004         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005         this.sm = this.selModel;
6006         this.sm.xmodule = this.xmodule || false;
6007     }
6008     
6009     if (this.cm && typeof(this.cm.config) == 'undefined') {
6010         this.colModel = new Roo.grid.ColumnModel(this.cm);
6011         this.cm = this.colModel;
6012         this.cm.xmodule = this.xmodule || false;
6013     }
6014     if (this.store) {
6015         this.store= Roo.factory(this.store, Roo.data);
6016         this.ds = this.store;
6017         this.ds.xmodule = this.xmodule || false;
6018          
6019     }
6020     if (this.footer && this.store) {
6021         this.footer.dataSource = this.ds;
6022         this.footer = Roo.factory(this.footer);
6023     }
6024     
6025     /** @private */
6026     this.addEvents({
6027         /**
6028          * @event cellclick
6029          * Fires when a cell is clicked
6030          * @param {Roo.bootstrap.Table} this
6031          * @param {Roo.Element} el
6032          * @param {Number} rowIndex
6033          * @param {Number} columnIndex
6034          * @param {Roo.EventObject} e
6035          */
6036         "cellclick" : true,
6037         /**
6038          * @event celldblclick
6039          * Fires when a cell is double clicked
6040          * @param {Roo.bootstrap.Table} this
6041          * @param {Roo.Element} el
6042          * @param {Number} rowIndex
6043          * @param {Number} columnIndex
6044          * @param {Roo.EventObject} e
6045          */
6046         "celldblclick" : true,
6047         /**
6048          * @event rowclick
6049          * Fires when a row is clicked
6050          * @param {Roo.bootstrap.Table} this
6051          * @param {Roo.Element} el
6052          * @param {Number} rowIndex
6053          * @param {Roo.EventObject} e
6054          */
6055         "rowclick" : true,
6056         /**
6057          * @event rowdblclick
6058          * Fires when a row is double clicked
6059          * @param {Roo.bootstrap.Table} this
6060          * @param {Roo.Element} el
6061          * @param {Number} rowIndex
6062          * @param {Roo.EventObject} e
6063          */
6064         "rowdblclick" : true,
6065         /**
6066          * @event mouseover
6067          * Fires when a mouseover occur
6068          * @param {Roo.bootstrap.Table} this
6069          * @param {Roo.Element} el
6070          * @param {Number} rowIndex
6071          * @param {Number} columnIndex
6072          * @param {Roo.EventObject} e
6073          */
6074         "mouseover" : true,
6075         /**
6076          * @event mouseout
6077          * Fires when a mouseout occur
6078          * @param {Roo.bootstrap.Table} this
6079          * @param {Roo.Element} el
6080          * @param {Number} rowIndex
6081          * @param {Number} columnIndex
6082          * @param {Roo.EventObject} e
6083          */
6084         "mouseout" : true,
6085         /**
6086          * @event rowclass
6087          * Fires when a row is rendered, so you can change add a style to it.
6088          * @param {Roo.bootstrap.Table} this
6089          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6090          */
6091         'rowclass' : true,
6092           /**
6093          * @event rowsrendered
6094          * Fires when all the  rows have been rendered
6095          * @param {Roo.bootstrap.Table} this
6096          */
6097         'rowsrendered' : true,
6098         /**
6099          * @event contextmenu
6100          * The raw contextmenu event for the entire grid.
6101          * @param {Roo.EventObject} e
6102          */
6103         "contextmenu" : true,
6104         /**
6105          * @event rowcontextmenu
6106          * Fires when a row is right clicked
6107          * @param {Roo.bootstrap.Table} this
6108          * @param {Number} rowIndex
6109          * @param {Roo.EventObject} e
6110          */
6111         "rowcontextmenu" : true,
6112         /**
6113          * @event cellcontextmenu
6114          * Fires when a cell is right clicked
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Number} rowIndex
6117          * @param {Number} cellIndex
6118          * @param {Roo.EventObject} e
6119          */
6120          "cellcontextmenu" : true,
6121          /**
6122          * @event headercontextmenu
6123          * Fires when a header is right clicked
6124          * @param {Roo.bootstrap.Table} this
6125          * @param {Number} columnIndex
6126          * @param {Roo.EventObject} e
6127          */
6128         "headercontextmenu" : true
6129     });
6130 };
6131
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6133     
6134     cls: false,
6135     align: false,
6136     bgcolor: false,
6137     border: false,
6138     cellpadding: false,
6139     cellspacing: false,
6140     frame: false,
6141     rules: false,
6142     sortable: false,
6143     summary: false,
6144     width: false,
6145     striped : false,
6146     scrollBody : false,
6147     bordered: false,
6148     hover:  false,
6149     condensed : false,
6150     responsive : false,
6151     sm : false,
6152     cm : false,
6153     store : false,
6154     loadMask : false,
6155     footerShow : true,
6156     headerShow : true,
6157   
6158     rowSelection : false,
6159     cellSelection : false,
6160     layout : false,
6161     
6162     // Roo.Element - the tbody
6163     mainBody: false,
6164     // Roo.Element - thead element
6165     mainHead: false,
6166     
6167     container: false, // used by gridpanel...
6168     
6169     lazyLoad : false,
6170     
6171     CSS : Roo.util.CSS,
6172     
6173     auto_hide_footer : false,
6174     
6175     getAutoCreate : function()
6176     {
6177         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'table',
6181             cls : 'table',
6182             cn : []
6183         };
6184         if (this.scrollBody) {
6185             cfg.cls += ' table-body-fixed';
6186         }    
6187         if (this.striped) {
6188             cfg.cls += ' table-striped';
6189         }
6190         
6191         if (this.hover) {
6192             cfg.cls += ' table-hover';
6193         }
6194         if (this.bordered) {
6195             cfg.cls += ' table-bordered';
6196         }
6197         if (this.condensed) {
6198             cfg.cls += ' table-condensed';
6199         }
6200         if (this.responsive) {
6201             cfg.cls += ' table-responsive';
6202         }
6203         
6204         if (this.cls) {
6205             cfg.cls+=  ' ' +this.cls;
6206         }
6207         
6208         // this lot should be simplifed...
6209         var _t = this;
6210         var cp = [
6211             'align',
6212             'bgcolor',
6213             'border',
6214             'cellpadding',
6215             'cellspacing',
6216             'frame',
6217             'rules',
6218             'sortable',
6219             'summary',
6220             'width'
6221         ].forEach(function(k) {
6222             if (_t[k]) {
6223                 cfg[k] = _t[k];
6224             }
6225         });
6226         
6227         
6228         if (this.layout) {
6229             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6230         }
6231         
6232         if(this.store || this.cm){
6233             if(this.headerShow){
6234                 cfg.cn.push(this.renderHeader());
6235             }
6236             
6237             cfg.cn.push(this.renderBody());
6238             
6239             if(this.footerShow){
6240                 cfg.cn.push(this.renderFooter());
6241             }
6242             // where does this come from?
6243             //cfg.cls+=  ' TableGrid';
6244         }
6245         
6246         return { cn : [ cfg ] };
6247     },
6248     
6249     initEvents : function()
6250     {   
6251         if(!this.store || !this.cm){
6252             return;
6253         }
6254         if (this.selModel) {
6255             this.selModel.initEvents();
6256         }
6257         
6258         
6259         //Roo.log('initEvents with ds!!!!');
6260         
6261         this.mainBody = this.el.select('tbody', true).first();
6262         this.mainHead = this.el.select('thead', true).first();
6263         this.mainFoot = this.el.select('tfoot', true).first();
6264         
6265         
6266         
6267         var _this = this;
6268         
6269         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270             e.on('click', _this.sort, _this);
6271         });
6272         
6273         this.mainBody.on("click", this.onClick, this);
6274         this.mainBody.on("dblclick", this.onDblClick, this);
6275         
6276         // why is this done????? = it breaks dialogs??
6277         //this.parent().el.setStyle('position', 'relative');
6278         
6279         
6280         if (this.footer) {
6281             this.footer.parentId = this.id;
6282             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6283             
6284             if(this.lazyLoad){
6285                 this.el.select('tfoot tr td').first().addClass('hide');
6286             }
6287         } 
6288         
6289         if(this.loadMask) {
6290             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6291         }
6292         
6293         this.store.on('load', this.onLoad, this);
6294         this.store.on('beforeload', this.onBeforeLoad, this);
6295         this.store.on('update', this.onUpdate, this);
6296         this.store.on('add', this.onAdd, this);
6297         this.store.on("clear", this.clear, this);
6298         
6299         this.el.on("contextmenu", this.onContextMenu, this);
6300         
6301         this.mainBody.on('scroll', this.onBodyScroll, this);
6302         
6303         this.cm.on("headerchange", this.onHeaderChange, this);
6304         
6305         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6306         
6307     },
6308     
6309     onContextMenu : function(e, t)
6310     {
6311         this.processEvent("contextmenu", e);
6312     },
6313     
6314     processEvent : function(name, e)
6315     {
6316         if (name != 'touchstart' ) {
6317             this.fireEvent(name, e);    
6318         }
6319         
6320         var t = e.getTarget();
6321         
6322         var cell = Roo.get(t);
6323         
6324         if(!cell){
6325             return;
6326         }
6327         
6328         if(cell.findParent('tfoot', false, true)){
6329             return;
6330         }
6331         
6332         if(cell.findParent('thead', false, true)){
6333             
6334             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335                 cell = Roo.get(t).findParent('th', false, true);
6336                 if (!cell) {
6337                     Roo.log("failed to find th in thead?");
6338                     Roo.log(e.getTarget());
6339                     return;
6340                 }
6341             }
6342             
6343             var cellIndex = cell.dom.cellIndex;
6344             
6345             var ename = name == 'touchstart' ? 'click' : name;
6346             this.fireEvent("header" + ename, this, cellIndex, e);
6347             
6348             return;
6349         }
6350         
6351         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352             cell = Roo.get(t).findParent('td', false, true);
6353             if (!cell) {
6354                 Roo.log("failed to find th in tbody?");
6355                 Roo.log(e.getTarget());
6356                 return;
6357             }
6358         }
6359         
6360         var row = cell.findParent('tr', false, true);
6361         var cellIndex = cell.dom.cellIndex;
6362         var rowIndex = row.dom.rowIndex - 1;
6363         
6364         if(row !== false){
6365             
6366             this.fireEvent("row" + name, this, rowIndex, e);
6367             
6368             if(cell !== false){
6369             
6370                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6371             }
6372         }
6373         
6374     },
6375     
6376     onMouseover : function(e, el)
6377     {
6378         var cell = Roo.get(el);
6379         
6380         if(!cell){
6381             return;
6382         }
6383         
6384         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385             cell = cell.findParent('td', false, true);
6386         }
6387         
6388         var row = cell.findParent('tr', false, true);
6389         var cellIndex = cell.dom.cellIndex;
6390         var rowIndex = row.dom.rowIndex - 1; // start from 0
6391         
6392         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6393         
6394     },
6395     
6396     onMouseout : function(e, el)
6397     {
6398         var cell = Roo.get(el);
6399         
6400         if(!cell){
6401             return;
6402         }
6403         
6404         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405             cell = cell.findParent('td', false, true);
6406         }
6407         
6408         var row = cell.findParent('tr', false, true);
6409         var cellIndex = cell.dom.cellIndex;
6410         var rowIndex = row.dom.rowIndex - 1; // start from 0
6411         
6412         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6413         
6414     },
6415     
6416     onClick : function(e, el)
6417     {
6418         var cell = Roo.get(el);
6419         
6420         if(!cell || (!this.cellSelection && !this.rowSelection)){
6421             return;
6422         }
6423         
6424         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425             cell = cell.findParent('td', false, true);
6426         }
6427         
6428         if(!cell || typeof(cell) == 'undefined'){
6429             return;
6430         }
6431         
6432         var row = cell.findParent('tr', false, true);
6433         
6434         if(!row || typeof(row) == 'undefined'){
6435             return;
6436         }
6437         
6438         var cellIndex = cell.dom.cellIndex;
6439         var rowIndex = this.getRowIndex(row);
6440         
6441         // why??? - should these not be based on SelectionModel?
6442         if(this.cellSelection){
6443             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6444         }
6445         
6446         if(this.rowSelection){
6447             this.fireEvent('rowclick', this, row, rowIndex, e);
6448         }
6449         
6450         
6451     },
6452         
6453     onDblClick : function(e,el)
6454     {
6455         var cell = Roo.get(el);
6456         
6457         if(!cell || (!this.cellSelection && !this.rowSelection)){
6458             return;
6459         }
6460         
6461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462             cell = cell.findParent('td', false, true);
6463         }
6464         
6465         if(!cell || typeof(cell) == 'undefined'){
6466             return;
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         
6471         if(!row || typeof(row) == 'undefined'){
6472             return;
6473         }
6474         
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = this.getRowIndex(row);
6477         
6478         if(this.cellSelection){
6479             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6480         }
6481         
6482         if(this.rowSelection){
6483             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6484         }
6485     },
6486     
6487     sort : function(e,el)
6488     {
6489         var col = Roo.get(el);
6490         
6491         if(!col.hasClass('sortable')){
6492             return;
6493         }
6494         
6495         var sort = col.attr('sort');
6496         var dir = 'ASC';
6497         
6498         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6499             dir = 'DESC';
6500         }
6501         
6502         this.store.sortInfo = {field : sort, direction : dir};
6503         
6504         if (this.footer) {
6505             Roo.log("calling footer first");
6506             this.footer.onClick('first');
6507         } else {
6508         
6509             this.store.load({ params : { start : 0 } });
6510         }
6511     },
6512     
6513     renderHeader : function()
6514     {
6515         var header = {
6516             tag: 'thead',
6517             cn : []
6518         };
6519         
6520         var cm = this.cm;
6521         this.totalWidth = 0;
6522         
6523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6524             
6525             var config = cm.config[i];
6526             
6527             var c = {
6528                 tag: 'th',
6529                 cls : 'x-hcol-' + i,
6530                 style : '',
6531                 html: cm.getColumnHeader(i)
6532             };
6533             
6534             var hh = '';
6535             
6536             if(typeof(config.sortable) != 'undefined' && config.sortable){
6537                 c.cls = 'sortable';
6538                 c.html = '<i class="glyphicon"></i>' + c.html;
6539             }
6540             
6541             if(typeof(config.lgHeader) != 'undefined'){
6542                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6543             }
6544             
6545             if(typeof(config.mdHeader) != 'undefined'){
6546                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6547             }
6548             
6549             if(typeof(config.smHeader) != 'undefined'){
6550                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6551             }
6552             
6553             if(typeof(config.xsHeader) != 'undefined'){
6554                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6555             }
6556             
6557             if(hh.length){
6558                 c.html = hh;
6559             }
6560             
6561             if(typeof(config.tooltip) != 'undefined'){
6562                 c.tooltip = config.tooltip;
6563             }
6564             
6565             if(typeof(config.colspan) != 'undefined'){
6566                 c.colspan = config.colspan;
6567             }
6568             
6569             if(typeof(config.hidden) != 'undefined' && config.hidden){
6570                 c.style += ' display:none;';
6571             }
6572             
6573             if(typeof(config.dataIndex) != 'undefined'){
6574                 c.sort = config.dataIndex;
6575             }
6576             
6577            
6578             
6579             if(typeof(config.align) != 'undefined' && config.align.length){
6580                 c.style += ' text-align:' + config.align + ';';
6581             }
6582             
6583             if(typeof(config.width) != 'undefined'){
6584                 c.style += ' width:' + config.width + 'px;';
6585                 this.totalWidth += config.width;
6586             } else {
6587                 this.totalWidth += 100; // assume minimum of 100 per column?
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     c.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 c.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608             
6609             header.cn.push(c)
6610         }
6611         
6612         return header;
6613     },
6614     
6615     renderBody : function()
6616     {
6617         var body = {
6618             tag: 'tbody',
6619             cn : [
6620                 {
6621                     tag: 'tr',
6622                     cn : [
6623                         {
6624                             tag : 'td',
6625                             colspan :  this.cm.getColumnCount()
6626                         }
6627                     ]
6628                 }
6629             ]
6630         };
6631         
6632         return body;
6633     },
6634     
6635     renderFooter : function()
6636     {
6637         var footer = {
6638             tag: 'tfoot',
6639             cn : [
6640                 {
6641                     tag: 'tr',
6642                     cn : [
6643                         {
6644                             tag : 'td',
6645                             colspan :  this.cm.getColumnCount()
6646                         }
6647                     ]
6648                 }
6649             ]
6650         };
6651         
6652         return footer;
6653     },
6654     
6655     
6656     
6657     onLoad : function()
6658     {
6659 //        Roo.log('ds onload');
6660         this.clear();
6661         
6662         var _this = this;
6663         var cm = this.cm;
6664         var ds = this.store;
6665         
6666         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668             if (_this.store.sortInfo) {
6669                     
6670                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6672                 }
6673                 
6674                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6676                 }
6677             }
6678         });
6679         
6680         var tbody =  this.mainBody;
6681               
6682         if(ds.getCount() > 0){
6683             ds.data.each(function(d,rowIndex){
6684                 var row =  this.renderRow(cm, ds, rowIndex);
6685                 
6686                 tbody.createChild(row);
6687                 
6688                 var _this = this;
6689                 
6690                 if(row.cellObjects.length){
6691                     Roo.each(row.cellObjects, function(r){
6692                         _this.renderCellObject(r);
6693                     })
6694                 }
6695                 
6696             }, this);
6697         }
6698         
6699         var tfoot = this.el.select('tfoot', true).first();
6700         
6701         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6702             
6703             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6704             
6705             var total = this.ds.getTotalCount();
6706             
6707             if(this.footer.pageSize < total){
6708                 this.mainFoot.show();
6709             }
6710         }
6711         
6712         Roo.each(this.el.select('tbody td', true).elements, function(e){
6713             e.on('mouseover', _this.onMouseover, _this);
6714         });
6715         
6716         Roo.each(this.el.select('tbody td', true).elements, function(e){
6717             e.on('mouseout', _this.onMouseout, _this);
6718         });
6719         this.fireEvent('rowsrendered', this);
6720         
6721         this.autoSize();
6722     },
6723     
6724     
6725     onUpdate : function(ds,record)
6726     {
6727         this.refreshRow(record);
6728         this.autoSize();
6729     },
6730     
6731     onRemove : function(ds, record, index, isUpdate){
6732         if(isUpdate !== true){
6733             this.fireEvent("beforerowremoved", this, index, record);
6734         }
6735         var bt = this.mainBody.dom;
6736         
6737         var rows = this.el.select('tbody > tr', true).elements;
6738         
6739         if(typeof(rows[index]) != 'undefined'){
6740             bt.removeChild(rows[index].dom);
6741         }
6742         
6743 //        if(bt.rows[index]){
6744 //            bt.removeChild(bt.rows[index]);
6745 //        }
6746         
6747         if(isUpdate !== true){
6748             //this.stripeRows(index);
6749             //this.syncRowHeights(index, index);
6750             //this.layout();
6751             this.fireEvent("rowremoved", this, index, record);
6752         }
6753     },
6754     
6755     onAdd : function(ds, records, rowIndex)
6756     {
6757         //Roo.log('on Add called');
6758         // - note this does not handle multiple adding very well..
6759         var bt = this.mainBody.dom;
6760         for (var i =0 ; i < records.length;i++) {
6761             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762             //Roo.log(records[i]);
6763             //Roo.log(this.store.getAt(rowIndex+i));
6764             this.insertRow(this.store, rowIndex + i, false);
6765             return;
6766         }
6767         
6768     },
6769     
6770     
6771     refreshRow : function(record){
6772         var ds = this.store, index;
6773         if(typeof record == 'number'){
6774             index = record;
6775             record = ds.getAt(index);
6776         }else{
6777             index = ds.indexOf(record);
6778         }
6779         this.insertRow(ds, index, true);
6780         this.autoSize();
6781         this.onRemove(ds, record, index+1, true);
6782         this.autoSize();
6783         //this.syncRowHeights(index, index);
6784         //this.layout();
6785         this.fireEvent("rowupdated", this, index, record);
6786     },
6787     
6788     insertRow : function(dm, rowIndex, isUpdate){
6789         
6790         if(!isUpdate){
6791             this.fireEvent("beforerowsinserted", this, rowIndex);
6792         }
6793             //var s = this.getScrollState();
6794         var row = this.renderRow(this.cm, this.store, rowIndex);
6795         // insert before rowIndex..
6796         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6797         
6798         var _this = this;
6799                 
6800         if(row.cellObjects.length){
6801             Roo.each(row.cellObjects, function(r){
6802                 _this.renderCellObject(r);
6803             })
6804         }
6805             
6806         if(!isUpdate){
6807             this.fireEvent("rowsinserted", this, rowIndex);
6808             //this.syncRowHeights(firstRow, lastRow);
6809             //this.stripeRows(firstRow);
6810             //this.layout();
6811         }
6812         
6813     },
6814     
6815     
6816     getRowDom : function(rowIndex)
6817     {
6818         var rows = this.el.select('tbody > tr', true).elements;
6819         
6820         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6821         
6822     },
6823     // returns the object tree for a tr..
6824   
6825     
6826     renderRow : function(cm, ds, rowIndex) 
6827     {
6828         var d = ds.getAt(rowIndex);
6829         
6830         var row = {
6831             tag : 'tr',
6832             cls : 'x-row-' + rowIndex,
6833             cn : []
6834         };
6835             
6836         var cellObjects = [];
6837         
6838         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839             var config = cm.config[i];
6840             
6841             var renderer = cm.getRenderer(i);
6842             var value = '';
6843             var id = false;
6844             
6845             if(typeof(renderer) !== 'undefined'){
6846                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6847             }
6848             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849             // and are rendered into the cells after the row is rendered - using the id for the element.
6850             
6851             if(typeof(value) === 'object'){
6852                 id = Roo.id();
6853                 cellObjects.push({
6854                     container : id,
6855                     cfg : value 
6856                 })
6857             }
6858             
6859             var rowcfg = {
6860                 record: d,
6861                 rowIndex : rowIndex,
6862                 colIndex : i,
6863                 rowClass : ''
6864             };
6865
6866             this.fireEvent('rowclass', this, rowcfg);
6867             
6868             var td = {
6869                 tag: 'td',
6870                 cls : rowcfg.rowClass + ' x-col-' + i,
6871                 style: '',
6872                 html: (typeof(value) === 'object') ? '' : value
6873             };
6874             
6875             if (id) {
6876                 td.id = id;
6877             }
6878             
6879             if(typeof(config.colspan) != 'undefined'){
6880                 td.colspan = config.colspan;
6881             }
6882             
6883             if(typeof(config.hidden) != 'undefined' && config.hidden){
6884                 td.style += ' display:none;';
6885             }
6886             
6887             if(typeof(config.align) != 'undefined' && config.align.length){
6888                 td.style += ' text-align:' + config.align + ';';
6889             }
6890             if(typeof(config.valign) != 'undefined' && config.valign.length){
6891                 td.style += ' vertical-align:' + config.valign + ';';
6892             }
6893             
6894             if(typeof(config.width) != 'undefined'){
6895                 td.style += ' width:' +  config.width + 'px;';
6896             }
6897             
6898             if(typeof(config.cursor) != 'undefined'){
6899                 td.style += ' cursor:' +  config.cursor + ';';
6900             }
6901             
6902             if(typeof(config.cls) != 'undefined'){
6903                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6904             }
6905             
6906             ['xs','sm','md','lg'].map(function(size){
6907                 
6908                 if(typeof(config[size]) == 'undefined'){
6909                     return;
6910                 }
6911                 
6912                 if (!config[size]) { // 0 = hidden
6913                     td.cls += ' hidden-' + size;
6914                     return;
6915                 }
6916                 
6917                 td.cls += ' col-' + size + '-' + config[size];
6918
6919             });
6920             
6921             row.cn.push(td);
6922            
6923         }
6924         
6925         row.cellObjects = cellObjects;
6926         
6927         return row;
6928           
6929     },
6930     
6931     
6932     
6933     onBeforeLoad : function()
6934     {
6935         
6936     },
6937      /**
6938      * Remove all rows
6939      */
6940     clear : function()
6941     {
6942         this.el.select('tbody', true).first().dom.innerHTML = '';
6943     },
6944     /**
6945      * Show or hide a row.
6946      * @param {Number} rowIndex to show or hide
6947      * @param {Boolean} state hide
6948      */
6949     setRowVisibility : function(rowIndex, state)
6950     {
6951         var bt = this.mainBody.dom;
6952         
6953         var rows = this.el.select('tbody > tr', true).elements;
6954         
6955         if(typeof(rows[rowIndex]) == 'undefined'){
6956             return;
6957         }
6958         rows[rowIndex].dom.style.display = state ? '' : 'none';
6959     },
6960     
6961     
6962     getSelectionModel : function(){
6963         if(!this.selModel){
6964             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6965         }
6966         return this.selModel;
6967     },
6968     /*
6969      * Render the Roo.bootstrap object from renderder
6970      */
6971     renderCellObject : function(r)
6972     {
6973         var _this = this;
6974         
6975         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6976         
6977         var t = r.cfg.render(r.container);
6978         
6979         if(r.cfg.cn){
6980             Roo.each(r.cfg.cn, function(c){
6981                 var child = {
6982                     container: t.getChildContainer(),
6983                     cfg: c
6984                 };
6985                 _this.renderCellObject(child);
6986             })
6987         }
6988     },
6989     
6990     getRowIndex : function(row)
6991     {
6992         var rowIndex = -1;
6993         
6994         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6995             if(el != row){
6996                 return;
6997             }
6998             
6999             rowIndex = index;
7000         });
7001         
7002         return rowIndex;
7003     },
7004      /**
7005      * Returns the grid's underlying element = used by panel.Grid
7006      * @return {Element} The element
7007      */
7008     getGridEl : function(){
7009         return this.el;
7010     },
7011      /**
7012      * Forces a resize - used by panel.Grid
7013      * @return {Element} The element
7014      */
7015     autoSize : function()
7016     {
7017         //var ctr = Roo.get(this.container.dom.parentElement);
7018         var ctr = Roo.get(this.el.dom);
7019         
7020         var thd = this.getGridEl().select('thead',true).first();
7021         var tbd = this.getGridEl().select('tbody', true).first();
7022         var tfd = this.getGridEl().select('tfoot', true).first();
7023         
7024         var cw = ctr.getWidth();
7025         
7026         if (tbd) {
7027             
7028             tbd.setSize(ctr.getWidth(),
7029                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7030             );
7031             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7032             cw -= barsize;
7033         }
7034         cw = Math.max(cw, this.totalWidth);
7035         this.getGridEl().select('tr',true).setWidth(cw);
7036         // resize 'expandable coloumn?
7037         
7038         return; // we doe not have a view in this design..
7039         
7040     },
7041     onBodyScroll: function()
7042     {
7043         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7044         if(this.mainHead){
7045             this.mainHead.setStyle({
7046                 'position' : 'relative',
7047                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7048             });
7049         }
7050         
7051         if(this.lazyLoad){
7052             
7053             var scrollHeight = this.mainBody.dom.scrollHeight;
7054             
7055             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7056             
7057             var height = this.mainBody.getHeight();
7058             
7059             if(scrollHeight - height == scrollTop) {
7060                 
7061                 var total = this.ds.getTotalCount();
7062                 
7063                 if(this.footer.cursor + this.footer.pageSize < total){
7064                     
7065                     this.footer.ds.load({
7066                         params : {
7067                             start : this.footer.cursor + this.footer.pageSize,
7068                             limit : this.footer.pageSize
7069                         },
7070                         add : true
7071                     });
7072                 }
7073             }
7074             
7075         }
7076     },
7077     
7078     onHeaderChange : function()
7079     {
7080         var header = this.renderHeader();
7081         var table = this.el.select('table', true).first();
7082         
7083         this.mainHead.remove();
7084         this.mainHead = table.createChild(header, this.mainBody, false);
7085     },
7086     
7087     onHiddenChange : function(colModel, colIndex, hidden)
7088     {
7089         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7091         
7092         this.CSS.updateRule(thSelector, "display", "");
7093         this.CSS.updateRule(tdSelector, "display", "");
7094         
7095         if(hidden){
7096             this.CSS.updateRule(thSelector, "display", "none");
7097             this.CSS.updateRule(tdSelector, "display", "none");
7098         }
7099         
7100         this.onHeaderChange();
7101         this.onLoad();
7102     },
7103     
7104     setColumnWidth: function(col_index, width)
7105     {
7106         // width = "md-2 xs-2..."
7107         if(!this.colModel.config[col_index]) {
7108             return;
7109         }
7110         
7111         var w = width.split(" ");
7112         
7113         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7114         
7115         var h_rows = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7116         
7117         if(rows.length != h_rows.length) {
7118             return;
7119         }
7120         
7121         for(var i = 0; i < rows.length; i++) {
7122             
7123             for(var j = 0; w.length; j++) {
7124                 
7125                 var size_cls = w[j].split("-");
7126                 
7127                 if(!Number.isInteger(size_cls[1] * 1)) {
7128                     continue;
7129                 }
7130                 
7131                 if(!this.colModel.config[col_index][size_cls[0]]) {
7132                     continue;
7133                 }
7134                 
7135                 rows[i].classList.replace(
7136                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7137                     "col-"+size_cls[0]+"-"+size_cls[1]
7138                 );
7139                 
7140                 h_rows[i].classList.replace(
7141                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7142                     "col-"+size_cls[0]+"-"+size_cls[1]
7143                 );
7144                 
7145                 this.colModel.config[col_index][size_cls[0]] = size_cls[1]
7146             }
7147         }
7148     }
7149 });
7150
7151  
7152
7153  /*
7154  * - LGPL
7155  *
7156  * table cell
7157  * 
7158  */
7159
7160 /**
7161  * @class Roo.bootstrap.TableCell
7162  * @extends Roo.bootstrap.Component
7163  * Bootstrap TableCell class
7164  * @cfg {String} html cell contain text
7165  * @cfg {String} cls cell class
7166  * @cfg {String} tag cell tag (td|th) default td
7167  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7168  * @cfg {String} align Aligns the content in a cell
7169  * @cfg {String} axis Categorizes cells
7170  * @cfg {String} bgcolor Specifies the background color of a cell
7171  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7172  * @cfg {Number} colspan Specifies the number of columns a cell should span
7173  * @cfg {String} headers Specifies one or more header cells a cell is related to
7174  * @cfg {Number} height Sets the height of a cell
7175  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7176  * @cfg {Number} rowspan Sets the number of rows a cell should span
7177  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7178  * @cfg {String} valign Vertical aligns the content in a cell
7179  * @cfg {Number} width Specifies the width of a cell
7180  * 
7181  * @constructor
7182  * Create a new TableCell
7183  * @param {Object} config The config object
7184  */
7185
7186 Roo.bootstrap.TableCell = function(config){
7187     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7188 };
7189
7190 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7191     
7192     html: false,
7193     cls: false,
7194     tag: false,
7195     abbr: false,
7196     align: false,
7197     axis: false,
7198     bgcolor: false,
7199     charoff: false,
7200     colspan: false,
7201     headers: false,
7202     height: false,
7203     nowrap: false,
7204     rowspan: false,
7205     scope: false,
7206     valign: false,
7207     width: false,
7208     
7209     
7210     getAutoCreate : function(){
7211         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7212         
7213         cfg = {
7214             tag: 'td'
7215         };
7216         
7217         if(this.tag){
7218             cfg.tag = this.tag;
7219         }
7220         
7221         if (this.html) {
7222             cfg.html=this.html
7223         }
7224         if (this.cls) {
7225             cfg.cls=this.cls
7226         }
7227         if (this.abbr) {
7228             cfg.abbr=this.abbr
7229         }
7230         if (this.align) {
7231             cfg.align=this.align
7232         }
7233         if (this.axis) {
7234             cfg.axis=this.axis
7235         }
7236         if (this.bgcolor) {
7237             cfg.bgcolor=this.bgcolor
7238         }
7239         if (this.charoff) {
7240             cfg.charoff=this.charoff
7241         }
7242         if (this.colspan) {
7243             cfg.colspan=this.colspan
7244         }
7245         if (this.headers) {
7246             cfg.headers=this.headers
7247         }
7248         if (this.height) {
7249             cfg.height=this.height
7250         }
7251         if (this.nowrap) {
7252             cfg.nowrap=this.nowrap
7253         }
7254         if (this.rowspan) {
7255             cfg.rowspan=this.rowspan
7256         }
7257         if (this.scope) {
7258             cfg.scope=this.scope
7259         }
7260         if (this.valign) {
7261             cfg.valign=this.valign
7262         }
7263         if (this.width) {
7264             cfg.width=this.width
7265         }
7266         
7267         
7268         return cfg;
7269     }
7270    
7271 });
7272
7273  
7274
7275  /*
7276  * - LGPL
7277  *
7278  * table row
7279  * 
7280  */
7281
7282 /**
7283  * @class Roo.bootstrap.TableRow
7284  * @extends Roo.bootstrap.Component
7285  * Bootstrap TableRow class
7286  * @cfg {String} cls row class
7287  * @cfg {String} align Aligns the content in a table row
7288  * @cfg {String} bgcolor Specifies a background color for a table row
7289  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7290  * @cfg {String} valign Vertical aligns the content in a table row
7291  * 
7292  * @constructor
7293  * Create a new TableRow
7294  * @param {Object} config The config object
7295  */
7296
7297 Roo.bootstrap.TableRow = function(config){
7298     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7299 };
7300
7301 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7302     
7303     cls: false,
7304     align: false,
7305     bgcolor: false,
7306     charoff: false,
7307     valign: false,
7308     
7309     getAutoCreate : function(){
7310         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7311         
7312         cfg = {
7313             tag: 'tr'
7314         };
7315             
7316         if(this.cls){
7317             cfg.cls = this.cls;
7318         }
7319         if(this.align){
7320             cfg.align = this.align;
7321         }
7322         if(this.bgcolor){
7323             cfg.bgcolor = this.bgcolor;
7324         }
7325         if(this.charoff){
7326             cfg.charoff = this.charoff;
7327         }
7328         if(this.valign){
7329             cfg.valign = this.valign;
7330         }
7331         
7332         return cfg;
7333     }
7334    
7335 });
7336
7337  
7338
7339  /*
7340  * - LGPL
7341  *
7342  * table body
7343  * 
7344  */
7345
7346 /**
7347  * @class Roo.bootstrap.TableBody
7348  * @extends Roo.bootstrap.Component
7349  * Bootstrap TableBody class
7350  * @cfg {String} cls element class
7351  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7352  * @cfg {String} align Aligns the content inside the element
7353  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7354  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7355  * 
7356  * @constructor
7357  * Create a new TableBody
7358  * @param {Object} config The config object
7359  */
7360
7361 Roo.bootstrap.TableBody = function(config){
7362     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7363 };
7364
7365 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7366     
7367     cls: false,
7368     tag: false,
7369     align: false,
7370     charoff: false,
7371     valign: false,
7372     
7373     getAutoCreate : function(){
7374         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7375         
7376         cfg = {
7377             tag: 'tbody'
7378         };
7379             
7380         if (this.cls) {
7381             cfg.cls=this.cls
7382         }
7383         if(this.tag){
7384             cfg.tag = this.tag;
7385         }
7386         
7387         if(this.align){
7388             cfg.align = this.align;
7389         }
7390         if(this.charoff){
7391             cfg.charoff = this.charoff;
7392         }
7393         if(this.valign){
7394             cfg.valign = this.valign;
7395         }
7396         
7397         return cfg;
7398     }
7399     
7400     
7401 //    initEvents : function()
7402 //    {
7403 //        
7404 //        if(!this.store){
7405 //            return;
7406 //        }
7407 //        
7408 //        this.store = Roo.factory(this.store, Roo.data);
7409 //        this.store.on('load', this.onLoad, this);
7410 //        
7411 //        this.store.load();
7412 //        
7413 //    },
7414 //    
7415 //    onLoad: function () 
7416 //    {   
7417 //        this.fireEvent('load', this);
7418 //    }
7419 //    
7420 //   
7421 });
7422
7423  
7424
7425  /*
7426  * Based on:
7427  * Ext JS Library 1.1.1
7428  * Copyright(c) 2006-2007, Ext JS, LLC.
7429  *
7430  * Originally Released Under LGPL - original licence link has changed is not relivant.
7431  *
7432  * Fork - LGPL
7433  * <script type="text/javascript">
7434  */
7435
7436 // as we use this in bootstrap.
7437 Roo.namespace('Roo.form');
7438  /**
7439  * @class Roo.form.Action
7440  * Internal Class used to handle form actions
7441  * @constructor
7442  * @param {Roo.form.BasicForm} el The form element or its id
7443  * @param {Object} config Configuration options
7444  */
7445
7446  
7447  
7448 // define the action interface
7449 Roo.form.Action = function(form, options){
7450     this.form = form;
7451     this.options = options || {};
7452 };
7453 /**
7454  * Client Validation Failed
7455  * @const 
7456  */
7457 Roo.form.Action.CLIENT_INVALID = 'client';
7458 /**
7459  * Server Validation Failed
7460  * @const 
7461  */
7462 Roo.form.Action.SERVER_INVALID = 'server';
7463  /**
7464  * Connect to Server Failed
7465  * @const 
7466  */
7467 Roo.form.Action.CONNECT_FAILURE = 'connect';
7468 /**
7469  * Reading Data from Server Failed
7470  * @const 
7471  */
7472 Roo.form.Action.LOAD_FAILURE = 'load';
7473
7474 Roo.form.Action.prototype = {
7475     type : 'default',
7476     failureType : undefined,
7477     response : undefined,
7478     result : undefined,
7479
7480     // interface method
7481     run : function(options){
7482
7483     },
7484
7485     // interface method
7486     success : function(response){
7487
7488     },
7489
7490     // interface method
7491     handleResponse : function(response){
7492
7493     },
7494
7495     // default connection failure
7496     failure : function(response){
7497         
7498         this.response = response;
7499         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7500         this.form.afterAction(this, false);
7501     },
7502
7503     processResponse : function(response){
7504         this.response = response;
7505         if(!response.responseText){
7506             return true;
7507         }
7508         this.result = this.handleResponse(response);
7509         return this.result;
7510     },
7511
7512     // utility functions used internally
7513     getUrl : function(appendParams){
7514         var url = this.options.url || this.form.url || this.form.el.dom.action;
7515         if(appendParams){
7516             var p = this.getParams();
7517             if(p){
7518                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7519             }
7520         }
7521         return url;
7522     },
7523
7524     getMethod : function(){
7525         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7526     },
7527
7528     getParams : function(){
7529         var bp = this.form.baseParams;
7530         var p = this.options.params;
7531         if(p){
7532             if(typeof p == "object"){
7533                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7534             }else if(typeof p == 'string' && bp){
7535                 p += '&' + Roo.urlEncode(bp);
7536             }
7537         }else if(bp){
7538             p = Roo.urlEncode(bp);
7539         }
7540         return p;
7541     },
7542
7543     createCallback : function(){
7544         return {
7545             success: this.success,
7546             failure: this.failure,
7547             scope: this,
7548             timeout: (this.form.timeout*1000),
7549             upload: this.form.fileUpload ? this.success : undefined
7550         };
7551     }
7552 };
7553
7554 Roo.form.Action.Submit = function(form, options){
7555     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7556 };
7557
7558 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7559     type : 'submit',
7560
7561     haveProgress : false,
7562     uploadComplete : false,
7563     
7564     // uploadProgress indicator.
7565     uploadProgress : function()
7566     {
7567         if (!this.form.progressUrl) {
7568             return;
7569         }
7570         
7571         if (!this.haveProgress) {
7572             Roo.MessageBox.progress("Uploading", "Uploading");
7573         }
7574         if (this.uploadComplete) {
7575            Roo.MessageBox.hide();
7576            return;
7577         }
7578         
7579         this.haveProgress = true;
7580    
7581         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7582         
7583         var c = new Roo.data.Connection();
7584         c.request({
7585             url : this.form.progressUrl,
7586             params: {
7587                 id : uid
7588             },
7589             method: 'GET',
7590             success : function(req){
7591                //console.log(data);
7592                 var rdata = false;
7593                 var edata;
7594                 try  {
7595                    rdata = Roo.decode(req.responseText)
7596                 } catch (e) {
7597                     Roo.log("Invalid data from server..");
7598                     Roo.log(edata);
7599                     return;
7600                 }
7601                 if (!rdata || !rdata.success) {
7602                     Roo.log(rdata);
7603                     Roo.MessageBox.alert(Roo.encode(rdata));
7604                     return;
7605                 }
7606                 var data = rdata.data;
7607                 
7608                 if (this.uploadComplete) {
7609                    Roo.MessageBox.hide();
7610                    return;
7611                 }
7612                    
7613                 if (data){
7614                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7615                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7616                     );
7617                 }
7618                 this.uploadProgress.defer(2000,this);
7619             },
7620        
7621             failure: function(data) {
7622                 Roo.log('progress url failed ');
7623                 Roo.log(data);
7624             },
7625             scope : this
7626         });
7627            
7628     },
7629     
7630     
7631     run : function()
7632     {
7633         // run get Values on the form, so it syncs any secondary forms.
7634         this.form.getValues();
7635         
7636         var o = this.options;
7637         var method = this.getMethod();
7638         var isPost = method == 'POST';
7639         if(o.clientValidation === false || this.form.isValid()){
7640             
7641             if (this.form.progressUrl) {
7642                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7643                     (new Date() * 1) + '' + Math.random());
7644                     
7645             } 
7646             
7647             
7648             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7649                 form:this.form.el.dom,
7650                 url:this.getUrl(!isPost),
7651                 method: method,
7652                 params:isPost ? this.getParams() : null,
7653                 isUpload: this.form.fileUpload
7654             }));
7655             
7656             this.uploadProgress();
7657
7658         }else if (o.clientValidation !== false){ // client validation failed
7659             this.failureType = Roo.form.Action.CLIENT_INVALID;
7660             this.form.afterAction(this, false);
7661         }
7662     },
7663
7664     success : function(response)
7665     {
7666         this.uploadComplete= true;
7667         if (this.haveProgress) {
7668             Roo.MessageBox.hide();
7669         }
7670         
7671         
7672         var result = this.processResponse(response);
7673         if(result === true || result.success){
7674             this.form.afterAction(this, true);
7675             return;
7676         }
7677         if(result.errors){
7678             this.form.markInvalid(result.errors);
7679             this.failureType = Roo.form.Action.SERVER_INVALID;
7680         }
7681         this.form.afterAction(this, false);
7682     },
7683     failure : function(response)
7684     {
7685         this.uploadComplete= true;
7686         if (this.haveProgress) {
7687             Roo.MessageBox.hide();
7688         }
7689         
7690         this.response = response;
7691         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7692         this.form.afterAction(this, false);
7693     },
7694     
7695     handleResponse : function(response){
7696         if(this.form.errorReader){
7697             var rs = this.form.errorReader.read(response);
7698             var errors = [];
7699             if(rs.records){
7700                 for(var i = 0, len = rs.records.length; i < len; i++) {
7701                     var r = rs.records[i];
7702                     errors[i] = r.data;
7703                 }
7704             }
7705             if(errors.length < 1){
7706                 errors = null;
7707             }
7708             return {
7709                 success : rs.success,
7710                 errors : errors
7711             };
7712         }
7713         var ret = false;
7714         try {
7715             ret = Roo.decode(response.responseText);
7716         } catch (e) {
7717             ret = {
7718                 success: false,
7719                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7720                 errors : []
7721             };
7722         }
7723         return ret;
7724         
7725     }
7726 });
7727
7728
7729 Roo.form.Action.Load = function(form, options){
7730     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7731     this.reader = this.form.reader;
7732 };
7733
7734 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7735     type : 'load',
7736
7737     run : function(){
7738         
7739         Roo.Ajax.request(Roo.apply(
7740                 this.createCallback(), {
7741                     method:this.getMethod(),
7742                     url:this.getUrl(false),
7743                     params:this.getParams()
7744         }));
7745     },
7746
7747     success : function(response){
7748         
7749         var result = this.processResponse(response);
7750         if(result === true || !result.success || !result.data){
7751             this.failureType = Roo.form.Action.LOAD_FAILURE;
7752             this.form.afterAction(this, false);
7753             return;
7754         }
7755         this.form.clearInvalid();
7756         this.form.setValues(result.data);
7757         this.form.afterAction(this, true);
7758     },
7759
7760     handleResponse : function(response){
7761         if(this.form.reader){
7762             var rs = this.form.reader.read(response);
7763             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7764             return {
7765                 success : rs.success,
7766                 data : data
7767             };
7768         }
7769         return Roo.decode(response.responseText);
7770     }
7771 });
7772
7773 Roo.form.Action.ACTION_TYPES = {
7774     'load' : Roo.form.Action.Load,
7775     'submit' : Roo.form.Action.Submit
7776 };/*
7777  * - LGPL
7778  *
7779  * form
7780  *
7781  */
7782
7783 /**
7784  * @class Roo.bootstrap.Form
7785  * @extends Roo.bootstrap.Component
7786  * Bootstrap Form class
7787  * @cfg {String} method  GET | POST (default POST)
7788  * @cfg {String} labelAlign top | left (default top)
7789  * @cfg {String} align left  | right - for navbars
7790  * @cfg {Boolean} loadMask load mask when submit (default true)
7791
7792  *
7793  * @constructor
7794  * Create a new Form
7795  * @param {Object} config The config object
7796  */
7797
7798
7799 Roo.bootstrap.Form = function(config){
7800     
7801     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7802     
7803     Roo.bootstrap.Form.popover.apply();
7804     
7805     this.addEvents({
7806         /**
7807          * @event clientvalidation
7808          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7809          * @param {Form} this
7810          * @param {Boolean} valid true if the form has passed client-side validation
7811          */
7812         clientvalidation: true,
7813         /**
7814          * @event beforeaction
7815          * Fires before any action is performed. Return false to cancel the action.
7816          * @param {Form} this
7817          * @param {Action} action The action to be performed
7818          */
7819         beforeaction: true,
7820         /**
7821          * @event actionfailed
7822          * Fires when an action fails.
7823          * @param {Form} this
7824          * @param {Action} action The action that failed
7825          */
7826         actionfailed : true,
7827         /**
7828          * @event actioncomplete
7829          * Fires when an action is completed.
7830          * @param {Form} this
7831          * @param {Action} action The action that completed
7832          */
7833         actioncomplete : true
7834     });
7835 };
7836
7837 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7838
7839      /**
7840      * @cfg {String} method
7841      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7842      */
7843     method : 'POST',
7844     /**
7845      * @cfg {String} url
7846      * The URL to use for form actions if one isn't supplied in the action options.
7847      */
7848     /**
7849      * @cfg {Boolean} fileUpload
7850      * Set to true if this form is a file upload.
7851      */
7852
7853     /**
7854      * @cfg {Object} baseParams
7855      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7856      */
7857
7858     /**
7859      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7860      */
7861     timeout: 30,
7862     /**
7863      * @cfg {Sting} align (left|right) for navbar forms
7864      */
7865     align : 'left',
7866
7867     // private
7868     activeAction : null,
7869
7870     /**
7871      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7872      * element by passing it or its id or mask the form itself by passing in true.
7873      * @type Mixed
7874      */
7875     waitMsgTarget : false,
7876
7877     loadMask : true,
7878     
7879     /**
7880      * @cfg {Boolean} errorMask (true|false) default false
7881      */
7882     errorMask : false,
7883     
7884     /**
7885      * @cfg {Number} maskOffset Default 100
7886      */
7887     maskOffset : 100,
7888     
7889     /**
7890      * @cfg {Boolean} maskBody
7891      */
7892     maskBody : false,
7893
7894     getAutoCreate : function(){
7895
7896         var cfg = {
7897             tag: 'form',
7898             method : this.method || 'POST',
7899             id : this.id || Roo.id(),
7900             cls : ''
7901         };
7902         if (this.parent().xtype.match(/^Nav/)) {
7903             cfg.cls = 'navbar-form navbar-' + this.align;
7904
7905         }
7906
7907         if (this.labelAlign == 'left' ) {
7908             cfg.cls += ' form-horizontal';
7909         }
7910
7911
7912         return cfg;
7913     },
7914     initEvents : function()
7915     {
7916         this.el.on('submit', this.onSubmit, this);
7917         // this was added as random key presses on the form where triggering form submit.
7918         this.el.on('keypress', function(e) {
7919             if (e.getCharCode() != 13) {
7920                 return true;
7921             }
7922             // we might need to allow it for textareas.. and some other items.
7923             // check e.getTarget().
7924
7925             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7926                 return true;
7927             }
7928
7929             Roo.log("keypress blocked");
7930
7931             e.preventDefault();
7932             return false;
7933         });
7934         
7935     },
7936     // private
7937     onSubmit : function(e){
7938         e.stopEvent();
7939     },
7940
7941      /**
7942      * Returns true if client-side validation on the form is successful.
7943      * @return Boolean
7944      */
7945     isValid : function(){
7946         var items = this.getItems();
7947         var valid = true;
7948         var target = false;
7949         
7950         items.each(function(f){
7951             
7952             if(f.validate()){
7953                 return;
7954             }
7955             
7956             Roo.log('invalid field: ' + f.name);
7957             
7958             valid = false;
7959
7960             if(!target && f.el.isVisible(true)){
7961                 target = f;
7962             }
7963            
7964         });
7965         
7966         if(this.errorMask && !valid){
7967             Roo.bootstrap.Form.popover.mask(this, target);
7968         }
7969         
7970         return valid;
7971     },
7972     
7973     /**
7974      * Returns true if any fields in this form have changed since their original load.
7975      * @return Boolean
7976      */
7977     isDirty : function(){
7978         var dirty = false;
7979         var items = this.getItems();
7980         items.each(function(f){
7981            if(f.isDirty()){
7982                dirty = true;
7983                return false;
7984            }
7985            return true;
7986         });
7987         return dirty;
7988     },
7989      /**
7990      * Performs a predefined action (submit or load) or custom actions you define on this form.
7991      * @param {String} actionName The name of the action type
7992      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7993      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7994      * accept other config options):
7995      * <pre>
7996 Property          Type             Description
7997 ----------------  ---------------  ----------------------------------------------------------------------------------
7998 url               String           The url for the action (defaults to the form's url)
7999 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8000 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8001 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8002                                    validate the form on the client (defaults to false)
8003      * </pre>
8004      * @return {BasicForm} this
8005      */
8006     doAction : function(action, options){
8007         if(typeof action == 'string'){
8008             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8009         }
8010         if(this.fireEvent('beforeaction', this, action) !== false){
8011             this.beforeAction(action);
8012             action.run.defer(100, action);
8013         }
8014         return this;
8015     },
8016
8017     // private
8018     beforeAction : function(action){
8019         var o = action.options;
8020         
8021         if(this.loadMask){
8022             
8023             if(this.maskBody){
8024                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8025             } else {
8026                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8027             }
8028         }
8029         // not really supported yet.. ??
8030
8031         //if(this.waitMsgTarget === true){
8032         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8033         //}else if(this.waitMsgTarget){
8034         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8035         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8036         //}else {
8037         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8038        // }
8039
8040     },
8041
8042     // private
8043     afterAction : function(action, success){
8044         this.activeAction = null;
8045         var o = action.options;
8046
8047         if(this.loadMask){
8048             
8049             if(this.maskBody){
8050                 Roo.get(document.body).unmask();
8051             } else {
8052                 this.el.unmask();
8053             }
8054         }
8055         
8056         //if(this.waitMsgTarget === true){
8057 //            this.el.unmask();
8058         //}else if(this.waitMsgTarget){
8059         //    this.waitMsgTarget.unmask();
8060         //}else{
8061         //    Roo.MessageBox.updateProgress(1);
8062         //    Roo.MessageBox.hide();
8063        // }
8064         //
8065         if(success){
8066             if(o.reset){
8067                 this.reset();
8068             }
8069             Roo.callback(o.success, o.scope, [this, action]);
8070             this.fireEvent('actioncomplete', this, action);
8071
8072         }else{
8073
8074             // failure condition..
8075             // we have a scenario where updates need confirming.
8076             // eg. if a locking scenario exists..
8077             // we look for { errors : { needs_confirm : true }} in the response.
8078             if (
8079                 (typeof(action.result) != 'undefined')  &&
8080                 (typeof(action.result.errors) != 'undefined')  &&
8081                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8082            ){
8083                 var _t = this;
8084                 Roo.log("not supported yet");
8085                  /*
8086
8087                 Roo.MessageBox.confirm(
8088                     "Change requires confirmation",
8089                     action.result.errorMsg,
8090                     function(r) {
8091                         if (r != 'yes') {
8092                             return;
8093                         }
8094                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8095                     }
8096
8097                 );
8098                 */
8099
8100
8101                 return;
8102             }
8103
8104             Roo.callback(o.failure, o.scope, [this, action]);
8105             // show an error message if no failed handler is set..
8106             if (!this.hasListener('actionfailed')) {
8107                 Roo.log("need to add dialog support");
8108                 /*
8109                 Roo.MessageBox.alert("Error",
8110                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8111                         action.result.errorMsg :
8112                         "Saving Failed, please check your entries or try again"
8113                 );
8114                 */
8115             }
8116
8117             this.fireEvent('actionfailed', this, action);
8118         }
8119
8120     },
8121     /**
8122      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8123      * @param {String} id The value to search for
8124      * @return Field
8125      */
8126     findField : function(id){
8127         var items = this.getItems();
8128         var field = items.get(id);
8129         if(!field){
8130              items.each(function(f){
8131                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8132                     field = f;
8133                     return false;
8134                 }
8135                 return true;
8136             });
8137         }
8138         return field || null;
8139     },
8140      /**
8141      * Mark fields in this form invalid in bulk.
8142      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8143      * @return {BasicForm} this
8144      */
8145     markInvalid : function(errors){
8146         if(errors instanceof Array){
8147             for(var i = 0, len = errors.length; i < len; i++){
8148                 var fieldError = errors[i];
8149                 var f = this.findField(fieldError.id);
8150                 if(f){
8151                     f.markInvalid(fieldError.msg);
8152                 }
8153             }
8154         }else{
8155             var field, id;
8156             for(id in errors){
8157                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8158                     field.markInvalid(errors[id]);
8159                 }
8160             }
8161         }
8162         //Roo.each(this.childForms || [], function (f) {
8163         //    f.markInvalid(errors);
8164         //});
8165
8166         return this;
8167     },
8168
8169     /**
8170      * Set values for fields in this form in bulk.
8171      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8172      * @return {BasicForm} this
8173      */
8174     setValues : function(values){
8175         if(values instanceof Array){ // array of objects
8176             for(var i = 0, len = values.length; i < len; i++){
8177                 var v = values[i];
8178                 var f = this.findField(v.id);
8179                 if(f){
8180                     f.setValue(v.value);
8181                     if(this.trackResetOnLoad){
8182                         f.originalValue = f.getValue();
8183                     }
8184                 }
8185             }
8186         }else{ // object hash
8187             var field, id;
8188             for(id in values){
8189                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8190
8191                     if (field.setFromData &&
8192                         field.valueField &&
8193                         field.displayField &&
8194                         // combos' with local stores can
8195                         // be queried via setValue()
8196                         // to set their value..
8197                         (field.store && !field.store.isLocal)
8198                         ) {
8199                         // it's a combo
8200                         var sd = { };
8201                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8202                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8203                         field.setFromData(sd);
8204
8205                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8206                         
8207                         field.setFromData(values);
8208                         
8209                     } else {
8210                         field.setValue(values[id]);
8211                     }
8212
8213
8214                     if(this.trackResetOnLoad){
8215                         field.originalValue = field.getValue();
8216                     }
8217                 }
8218             }
8219         }
8220
8221         //Roo.each(this.childForms || [], function (f) {
8222         //    f.setValues(values);
8223         //});
8224
8225         return this;
8226     },
8227
8228     /**
8229      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8230      * they are returned as an array.
8231      * @param {Boolean} asString
8232      * @return {Object}
8233      */
8234     getValues : function(asString){
8235         //if (this.childForms) {
8236             // copy values from the child forms
8237         //    Roo.each(this.childForms, function (f) {
8238         //        this.setValues(f.getValues());
8239         //    }, this);
8240         //}
8241
8242
8243
8244         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8245         if(asString === true){
8246             return fs;
8247         }
8248         return Roo.urlDecode(fs);
8249     },
8250
8251     /**
8252      * Returns the fields in this form as an object with key/value pairs.
8253      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8254      * @return {Object}
8255      */
8256     getFieldValues : function(with_hidden)
8257     {
8258         var items = this.getItems();
8259         var ret = {};
8260         items.each(function(f){
8261             
8262             if (!f.getName()) {
8263                 return;
8264             }
8265             
8266             var v = f.getValue();
8267             
8268             if (f.inputType =='radio') {
8269                 if (typeof(ret[f.getName()]) == 'undefined') {
8270                     ret[f.getName()] = ''; // empty..
8271                 }
8272
8273                 if (!f.el.dom.checked) {
8274                     return;
8275
8276                 }
8277                 v = f.el.dom.value;
8278
8279             }
8280             
8281             if(f.xtype == 'MoneyField'){
8282                 ret[f.currencyName] = f.getCurrency();
8283             }
8284
8285             // not sure if this supported any more..
8286             if ((typeof(v) == 'object') && f.getRawValue) {
8287                 v = f.getRawValue() ; // dates..
8288             }
8289             // combo boxes where name != hiddenName...
8290             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8291                 ret[f.name] = f.getRawValue();
8292             }
8293             ret[f.getName()] = v;
8294         });
8295
8296         return ret;
8297     },
8298
8299     /**
8300      * Clears all invalid messages in this form.
8301      * @return {BasicForm} this
8302      */
8303     clearInvalid : function(){
8304         var items = this.getItems();
8305
8306         items.each(function(f){
8307            f.clearInvalid();
8308         });
8309
8310         return this;
8311     },
8312
8313     /**
8314      * Resets this form.
8315      * @return {BasicForm} this
8316      */
8317     reset : function(){
8318         var items = this.getItems();
8319         items.each(function(f){
8320             f.reset();
8321         });
8322
8323         Roo.each(this.childForms || [], function (f) {
8324             f.reset();
8325         });
8326
8327
8328         return this;
8329     },
8330     
8331     getItems : function()
8332     {
8333         var r=new Roo.util.MixedCollection(false, function(o){
8334             return o.id || (o.id = Roo.id());
8335         });
8336         var iter = function(el) {
8337             if (el.inputEl) {
8338                 r.add(el);
8339             }
8340             if (!el.items) {
8341                 return;
8342             }
8343             Roo.each(el.items,function(e) {
8344                 iter(e);
8345             });
8346         };
8347
8348         iter(this);
8349         return r;
8350     },
8351     
8352     hideFields : function(items)
8353     {
8354         Roo.each(items, function(i){
8355             
8356             var f = this.findField(i);
8357             
8358             if(!f){
8359                 return;
8360             }
8361             
8362             f.hide();
8363             
8364         }, this);
8365     },
8366     
8367     showFields : function(items)
8368     {
8369         Roo.each(items, function(i){
8370             
8371             var f = this.findField(i);
8372             
8373             if(!f){
8374                 return;
8375             }
8376             
8377             f.show();
8378             
8379         }, this);
8380     }
8381
8382 });
8383
8384 Roo.apply(Roo.bootstrap.Form, {
8385     
8386     popover : {
8387         
8388         padding : 5,
8389         
8390         isApplied : false,
8391         
8392         isMasked : false,
8393         
8394         form : false,
8395         
8396         target : false,
8397         
8398         toolTip : false,
8399         
8400         intervalID : false,
8401         
8402         maskEl : false,
8403         
8404         apply : function()
8405         {
8406             if(this.isApplied){
8407                 return;
8408             }
8409             
8410             this.maskEl = {
8411                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8412                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8413                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8414                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8415             };
8416             
8417             this.maskEl.top.enableDisplayMode("block");
8418             this.maskEl.left.enableDisplayMode("block");
8419             this.maskEl.bottom.enableDisplayMode("block");
8420             this.maskEl.right.enableDisplayMode("block");
8421             
8422             this.toolTip = new Roo.bootstrap.Tooltip({
8423                 cls : 'roo-form-error-popover',
8424                 alignment : {
8425                     'left' : ['r-l', [-2,0], 'right'],
8426                     'right' : ['l-r', [2,0], 'left'],
8427                     'bottom' : ['tl-bl', [0,2], 'top'],
8428                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8429                 }
8430             });
8431             
8432             this.toolTip.render(Roo.get(document.body));
8433
8434             this.toolTip.el.enableDisplayMode("block");
8435             
8436             Roo.get(document.body).on('click', function(){
8437                 this.unmask();
8438             }, this);
8439             
8440             Roo.get(document.body).on('touchstart', function(){
8441                 this.unmask();
8442             }, this);
8443             
8444             this.isApplied = true
8445         },
8446         
8447         mask : function(form, target)
8448         {
8449             this.form = form;
8450             
8451             this.target = target;
8452             
8453             if(!this.form.errorMask || !target.el){
8454                 return;
8455             }
8456             
8457             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8458             
8459             Roo.log(scrollable);
8460             
8461             var ot = this.target.el.calcOffsetsTo(scrollable);
8462             
8463             var scrollTo = ot[1] - this.form.maskOffset;
8464             
8465             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8466             
8467             scrollable.scrollTo('top', scrollTo);
8468             
8469             var box = this.target.el.getBox();
8470             Roo.log(box);
8471             var zIndex = Roo.bootstrap.Modal.zIndex++;
8472
8473             
8474             this.maskEl.top.setStyle('position', 'absolute');
8475             this.maskEl.top.setStyle('z-index', zIndex);
8476             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8477             this.maskEl.top.setLeft(0);
8478             this.maskEl.top.setTop(0);
8479             this.maskEl.top.show();
8480             
8481             this.maskEl.left.setStyle('position', 'absolute');
8482             this.maskEl.left.setStyle('z-index', zIndex);
8483             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8484             this.maskEl.left.setLeft(0);
8485             this.maskEl.left.setTop(box.y - this.padding);
8486             this.maskEl.left.show();
8487
8488             this.maskEl.bottom.setStyle('position', 'absolute');
8489             this.maskEl.bottom.setStyle('z-index', zIndex);
8490             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8491             this.maskEl.bottom.setLeft(0);
8492             this.maskEl.bottom.setTop(box.bottom + this.padding);
8493             this.maskEl.bottom.show();
8494
8495             this.maskEl.right.setStyle('position', 'absolute');
8496             this.maskEl.right.setStyle('z-index', zIndex);
8497             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8498             this.maskEl.right.setLeft(box.right + this.padding);
8499             this.maskEl.right.setTop(box.y - this.padding);
8500             this.maskEl.right.show();
8501
8502             this.toolTip.bindEl = this.target.el;
8503
8504             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8505
8506             var tip = this.target.blankText;
8507
8508             if(this.target.getValue() !== '' ) {
8509                 
8510                 if (this.target.invalidText.length) {
8511                     tip = this.target.invalidText;
8512                 } else if (this.target.regexText.length){
8513                     tip = this.target.regexText;
8514                 }
8515             }
8516
8517             this.toolTip.show(tip);
8518
8519             this.intervalID = window.setInterval(function() {
8520                 Roo.bootstrap.Form.popover.unmask();
8521             }, 10000);
8522
8523             window.onwheel = function(){ return false;};
8524             
8525             (function(){ this.isMasked = true; }).defer(500, this);
8526             
8527         },
8528         
8529         unmask : function()
8530         {
8531             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8532                 return;
8533             }
8534             
8535             this.maskEl.top.setStyle('position', 'absolute');
8536             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8537             this.maskEl.top.hide();
8538
8539             this.maskEl.left.setStyle('position', 'absolute');
8540             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8541             this.maskEl.left.hide();
8542
8543             this.maskEl.bottom.setStyle('position', 'absolute');
8544             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8545             this.maskEl.bottom.hide();
8546
8547             this.maskEl.right.setStyle('position', 'absolute');
8548             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8549             this.maskEl.right.hide();
8550             
8551             this.toolTip.hide();
8552             
8553             this.toolTip.el.hide();
8554             
8555             window.onwheel = function(){ return true;};
8556             
8557             if(this.intervalID){
8558                 window.clearInterval(this.intervalID);
8559                 this.intervalID = false;
8560             }
8561             
8562             this.isMasked = false;
8563             
8564         }
8565         
8566     }
8567     
8568 });
8569
8570 /*
8571  * Based on:
8572  * Ext JS Library 1.1.1
8573  * Copyright(c) 2006-2007, Ext JS, LLC.
8574  *
8575  * Originally Released Under LGPL - original licence link has changed is not relivant.
8576  *
8577  * Fork - LGPL
8578  * <script type="text/javascript">
8579  */
8580 /**
8581  * @class Roo.form.VTypes
8582  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8583  * @singleton
8584  */
8585 Roo.form.VTypes = function(){
8586     // closure these in so they are only created once.
8587     var alpha = /^[a-zA-Z_]+$/;
8588     var alphanum = /^[a-zA-Z0-9_]+$/;
8589     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8590     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8591
8592     // All these messages and functions are configurable
8593     return {
8594         /**
8595          * The function used to validate email addresses
8596          * @param {String} value The email address
8597          */
8598         'email' : function(v){
8599             return email.test(v);
8600         },
8601         /**
8602          * The error text to display when the email validation function returns false
8603          * @type String
8604          */
8605         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8606         /**
8607          * The keystroke filter mask to be applied on email input
8608          * @type RegExp
8609          */
8610         'emailMask' : /[a-z0-9_\.\-@]/i,
8611
8612         /**
8613          * The function used to validate URLs
8614          * @param {String} value The URL
8615          */
8616         'url' : function(v){
8617             return url.test(v);
8618         },
8619         /**
8620          * The error text to display when the url validation function returns false
8621          * @type String
8622          */
8623         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8624         
8625         /**
8626          * The function used to validate alpha values
8627          * @param {String} value The value
8628          */
8629         'alpha' : function(v){
8630             return alpha.test(v);
8631         },
8632         /**
8633          * The error text to display when the alpha validation function returns false
8634          * @type String
8635          */
8636         'alphaText' : 'This field should only contain letters and _',
8637         /**
8638          * The keystroke filter mask to be applied on alpha input
8639          * @type RegExp
8640          */
8641         'alphaMask' : /[a-z_]/i,
8642
8643         /**
8644          * The function used to validate alphanumeric values
8645          * @param {String} value The value
8646          */
8647         'alphanum' : function(v){
8648             return alphanum.test(v);
8649         },
8650         /**
8651          * The error text to display when the alphanumeric validation function returns false
8652          * @type String
8653          */
8654         'alphanumText' : 'This field should only contain letters, numbers and _',
8655         /**
8656          * The keystroke filter mask to be applied on alphanumeric input
8657          * @type RegExp
8658          */
8659         'alphanumMask' : /[a-z0-9_]/i
8660     };
8661 }();/*
8662  * - LGPL
8663  *
8664  * Input
8665  * 
8666  */
8667
8668 /**
8669  * @class Roo.bootstrap.Input
8670  * @extends Roo.bootstrap.Component
8671  * Bootstrap Input class
8672  * @cfg {Boolean} disabled is it disabled
8673  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8674  * @cfg {String} name name of the input
8675  * @cfg {string} fieldLabel - the label associated
8676  * @cfg {string} placeholder - placeholder to put in text.
8677  * @cfg {string}  before - input group add on before
8678  * @cfg {string} after - input group add on after
8679  * @cfg {string} size - (lg|sm) or leave empty..
8680  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8681  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8682  * @cfg {Number} md colspan out of 12 for computer-sized screens
8683  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8684  * @cfg {string} value default value of the input
8685  * @cfg {Number} labelWidth set the width of label 
8686  * @cfg {Number} labellg set the width of label (1-12)
8687  * @cfg {Number} labelmd set the width of label (1-12)
8688  * @cfg {Number} labelsm set the width of label (1-12)
8689  * @cfg {Number} labelxs set the width of label (1-12)
8690  * @cfg {String} labelAlign (top|left)
8691  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8692  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8693  * @cfg {String} indicatorpos (left|right) default left
8694  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8695  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8696
8697  * @cfg {String} align (left|center|right) Default left
8698  * @cfg {Boolean} forceFeedback (true|false) Default false
8699  * 
8700  * @constructor
8701  * Create a new Input
8702  * @param {Object} config The config object
8703  */
8704
8705 Roo.bootstrap.Input = function(config){
8706     
8707     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8708     
8709     this.addEvents({
8710         /**
8711          * @event focus
8712          * Fires when this field receives input focus.
8713          * @param {Roo.form.Field} this
8714          */
8715         focus : true,
8716         /**
8717          * @event blur
8718          * Fires when this field loses input focus.
8719          * @param {Roo.form.Field} this
8720          */
8721         blur : true,
8722         /**
8723          * @event specialkey
8724          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8725          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8726          * @param {Roo.form.Field} this
8727          * @param {Roo.EventObject} e The event object
8728          */
8729         specialkey : true,
8730         /**
8731          * @event change
8732          * Fires just before the field blurs if the field value has changed.
8733          * @param {Roo.form.Field} this
8734          * @param {Mixed} newValue The new value
8735          * @param {Mixed} oldValue The original value
8736          */
8737         change : true,
8738         /**
8739          * @event invalid
8740          * Fires after the field has been marked as invalid.
8741          * @param {Roo.form.Field} this
8742          * @param {String} msg The validation message
8743          */
8744         invalid : true,
8745         /**
8746          * @event valid
8747          * Fires after the field has been validated with no errors.
8748          * @param {Roo.form.Field} this
8749          */
8750         valid : true,
8751          /**
8752          * @event keyup
8753          * Fires after the key up
8754          * @param {Roo.form.Field} this
8755          * @param {Roo.EventObject}  e The event Object
8756          */
8757         keyup : true
8758     });
8759 };
8760
8761 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8762      /**
8763      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8764       automatic validation (defaults to "keyup").
8765      */
8766     validationEvent : "keyup",
8767      /**
8768      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8769      */
8770     validateOnBlur : true,
8771     /**
8772      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8773      */
8774     validationDelay : 250,
8775      /**
8776      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8777      */
8778     focusClass : "x-form-focus",  // not needed???
8779     
8780        
8781     /**
8782      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8783      */
8784     invalidClass : "has-warning",
8785     
8786     /**
8787      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8788      */
8789     validClass : "has-success",
8790     
8791     /**
8792      * @cfg {Boolean} hasFeedback (true|false) default true
8793      */
8794     hasFeedback : true,
8795     
8796     /**
8797      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8798      */
8799     invalidFeedbackClass : "glyphicon-warning-sign",
8800     
8801     /**
8802      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8803      */
8804     validFeedbackClass : "glyphicon-ok",
8805     
8806     /**
8807      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8808      */
8809     selectOnFocus : false,
8810     
8811      /**
8812      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8813      */
8814     maskRe : null,
8815        /**
8816      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8817      */
8818     vtype : null,
8819     
8820       /**
8821      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8822      */
8823     disableKeyFilter : false,
8824     
8825        /**
8826      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8827      */
8828     disabled : false,
8829      /**
8830      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8831      */
8832     allowBlank : true,
8833     /**
8834      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8835      */
8836     blankText : "Please complete this mandatory field",
8837     
8838      /**
8839      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8840      */
8841     minLength : 0,
8842     /**
8843      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8844      */
8845     maxLength : Number.MAX_VALUE,
8846     /**
8847      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8848      */
8849     minLengthText : "The minimum length for this field is {0}",
8850     /**
8851      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8852      */
8853     maxLengthText : "The maximum length for this field is {0}",
8854   
8855     
8856     /**
8857      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8858      * If available, this function will be called only after the basic validators all return true, and will be passed the
8859      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8860      */
8861     validator : null,
8862     /**
8863      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8864      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8865      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8866      */
8867     regex : null,
8868     /**
8869      * @cfg {String} regexText -- Depricated - use Invalid Text
8870      */
8871     regexText : "",
8872     
8873     /**
8874      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8875      */
8876     invalidText : "",
8877     
8878     
8879     
8880     autocomplete: false,
8881     
8882     
8883     fieldLabel : '',
8884     inputType : 'text',
8885     
8886     name : false,
8887     placeholder: false,
8888     before : false,
8889     after : false,
8890     size : false,
8891     hasFocus : false,
8892     preventMark: false,
8893     isFormField : true,
8894     value : '',
8895     labelWidth : 2,
8896     labelAlign : false,
8897     readOnly : false,
8898     align : false,
8899     formatedValue : false,
8900     forceFeedback : false,
8901     
8902     indicatorpos : 'left',
8903     
8904     labellg : 0,
8905     labelmd : 0,
8906     labelsm : 0,
8907     labelxs : 0,
8908     
8909     capture : '',
8910     accept : '',
8911     
8912     parentLabelAlign : function()
8913     {
8914         var parent = this;
8915         while (parent.parent()) {
8916             parent = parent.parent();
8917             if (typeof(parent.labelAlign) !='undefined') {
8918                 return parent.labelAlign;
8919             }
8920         }
8921         return 'left';
8922         
8923     },
8924     
8925     getAutoCreate : function()
8926     {
8927         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8928         
8929         var id = Roo.id();
8930         
8931         var cfg = {};
8932         
8933         if(this.inputType != 'hidden'){
8934             cfg.cls = 'form-group' //input-group
8935         }
8936         
8937         var input =  {
8938             tag: 'input',
8939             id : id,
8940             type : this.inputType,
8941             value : this.value,
8942             cls : 'form-control',
8943             placeholder : this.placeholder || '',
8944             autocomplete : this.autocomplete || 'new-password'
8945         };
8946         
8947         if(this.capture.length){
8948             input.capture = this.capture;
8949         }
8950         
8951         if(this.accept.length){
8952             input.accept = this.accept + "/*";
8953         }
8954         
8955         if(this.align){
8956             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8957         }
8958         
8959         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8960             input.maxLength = this.maxLength;
8961         }
8962         
8963         if (this.disabled) {
8964             input.disabled=true;
8965         }
8966         
8967         if (this.readOnly) {
8968             input.readonly=true;
8969         }
8970         
8971         if (this.name) {
8972             input.name = this.name;
8973         }
8974         
8975         if (this.size) {
8976             input.cls += ' input-' + this.size;
8977         }
8978         
8979         var settings=this;
8980         ['xs','sm','md','lg'].map(function(size){
8981             if (settings[size]) {
8982                 cfg.cls += ' col-' + size + '-' + settings[size];
8983             }
8984         });
8985         
8986         var inputblock = input;
8987         
8988         var feedback = {
8989             tag: 'span',
8990             cls: 'glyphicon form-control-feedback'
8991         };
8992             
8993         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8994             
8995             inputblock = {
8996                 cls : 'has-feedback',
8997                 cn :  [
8998                     input,
8999                     feedback
9000                 ] 
9001             };  
9002         }
9003         
9004         if (this.before || this.after) {
9005             
9006             inputblock = {
9007                 cls : 'input-group',
9008                 cn :  [] 
9009             };
9010             
9011             if (this.before && typeof(this.before) == 'string') {
9012                 
9013                 inputblock.cn.push({
9014                     tag :'span',
9015                     cls : 'roo-input-before input-group-addon',
9016                     html : this.before
9017                 });
9018             }
9019             if (this.before && typeof(this.before) == 'object') {
9020                 this.before = Roo.factory(this.before);
9021                 
9022                 inputblock.cn.push({
9023                     tag :'span',
9024                     cls : 'roo-input-before input-group-' +
9025                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9026                 });
9027             }
9028             
9029             inputblock.cn.push(input);
9030             
9031             if (this.after && typeof(this.after) == 'string') {
9032                 inputblock.cn.push({
9033                     tag :'span',
9034                     cls : 'roo-input-after input-group-addon',
9035                     html : this.after
9036                 });
9037             }
9038             if (this.after && typeof(this.after) == 'object') {
9039                 this.after = Roo.factory(this.after);
9040                 
9041                 inputblock.cn.push({
9042                     tag :'span',
9043                     cls : 'roo-input-after input-group-' +
9044                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9045                 });
9046             }
9047             
9048             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9049                 inputblock.cls += ' has-feedback';
9050                 inputblock.cn.push(feedback);
9051             }
9052         };
9053         
9054         if (align ==='left' && this.fieldLabel.length) {
9055             
9056             cfg.cls += ' roo-form-group-label-left';
9057             
9058             cfg.cn = [
9059                 {
9060                     tag : 'i',
9061                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9062                     tooltip : 'This field is required'
9063                 },
9064                 {
9065                     tag: 'label',
9066                     'for' :  id,
9067                     cls : 'control-label',
9068                     html : this.fieldLabel
9069
9070                 },
9071                 {
9072                     cls : "", 
9073                     cn: [
9074                         inputblock
9075                     ]
9076                 }
9077             ];
9078             
9079             var labelCfg = cfg.cn[1];
9080             var contentCfg = cfg.cn[2];
9081             
9082             if(this.indicatorpos == 'right'){
9083                 cfg.cn = [
9084                     {
9085                         tag: 'label',
9086                         'for' :  id,
9087                         cls : 'control-label',
9088                         cn : [
9089                             {
9090                                 tag : 'span',
9091                                 html : this.fieldLabel
9092                             },
9093                             {
9094                                 tag : 'i',
9095                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9096                                 tooltip : 'This field is required'
9097                             }
9098                         ]
9099                     },
9100                     {
9101                         cls : "",
9102                         cn: [
9103                             inputblock
9104                         ]
9105                     }
9106
9107                 ];
9108                 
9109                 labelCfg = cfg.cn[0];
9110                 contentCfg = cfg.cn[1];
9111             
9112             }
9113             
9114             if(this.labelWidth > 12){
9115                 labelCfg.style = "width: " + this.labelWidth + 'px';
9116             }
9117             
9118             if(this.labelWidth < 13 && this.labelmd == 0){
9119                 this.labelmd = this.labelWidth;
9120             }
9121             
9122             if(this.labellg > 0){
9123                 labelCfg.cls += ' col-lg-' + this.labellg;
9124                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9125             }
9126             
9127             if(this.labelmd > 0){
9128                 labelCfg.cls += ' col-md-' + this.labelmd;
9129                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9130             }
9131             
9132             if(this.labelsm > 0){
9133                 labelCfg.cls += ' col-sm-' + this.labelsm;
9134                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9135             }
9136             
9137             if(this.labelxs > 0){
9138                 labelCfg.cls += ' col-xs-' + this.labelxs;
9139                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9140             }
9141             
9142             
9143         } else if ( this.fieldLabel.length) {
9144                 
9145             cfg.cn = [
9146                 {
9147                     tag : 'i',
9148                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9149                     tooltip : 'This field is required'
9150                 },
9151                 {
9152                     tag: 'label',
9153                    //cls : 'input-group-addon',
9154                     html : this.fieldLabel
9155
9156                 },
9157
9158                inputblock
9159
9160            ];
9161            
9162            if(this.indicatorpos == 'right'){
9163                 
9164                 cfg.cn = [
9165                     {
9166                         tag: 'label',
9167                        //cls : 'input-group-addon',
9168                         html : this.fieldLabel
9169
9170                     },
9171                     {
9172                         tag : 'i',
9173                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9174                         tooltip : 'This field is required'
9175                     },
9176
9177                    inputblock
9178
9179                ];
9180
9181             }
9182
9183         } else {
9184             
9185             cfg.cn = [
9186
9187                     inputblock
9188
9189             ];
9190                 
9191                 
9192         };
9193         
9194         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9195            cfg.cls += ' navbar-form';
9196         }
9197         
9198         if (this.parentType === 'NavGroup') {
9199            cfg.cls += ' navbar-form';
9200            cfg.tag = 'li';
9201         }
9202         
9203         return cfg;
9204         
9205     },
9206     /**
9207      * return the real input element.
9208      */
9209     inputEl: function ()
9210     {
9211         return this.el.select('input.form-control',true).first();
9212     },
9213     
9214     tooltipEl : function()
9215     {
9216         return this.inputEl();
9217     },
9218     
9219     indicatorEl : function()
9220     {
9221         var indicator = this.el.select('i.roo-required-indicator',true).first();
9222         
9223         if(!indicator){
9224             return false;
9225         }
9226         
9227         return indicator;
9228         
9229     },
9230     
9231     setDisabled : function(v)
9232     {
9233         var i  = this.inputEl().dom;
9234         if (!v) {
9235             i.removeAttribute('disabled');
9236             return;
9237             
9238         }
9239         i.setAttribute('disabled','true');
9240     },
9241     initEvents : function()
9242     {
9243           
9244         this.inputEl().on("keydown" , this.fireKey,  this);
9245         this.inputEl().on("focus", this.onFocus,  this);
9246         this.inputEl().on("blur", this.onBlur,  this);
9247         
9248         this.inputEl().relayEvent('keyup', this);
9249         
9250         this.indicator = this.indicatorEl();
9251         
9252         if(this.indicator){
9253             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9254         }
9255  
9256         // reference to original value for reset
9257         this.originalValue = this.getValue();
9258         //Roo.form.TextField.superclass.initEvents.call(this);
9259         if(this.validationEvent == 'keyup'){
9260             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9261             this.inputEl().on('keyup', this.filterValidation, this);
9262         }
9263         else if(this.validationEvent !== false){
9264             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9265         }
9266         
9267         if(this.selectOnFocus){
9268             this.on("focus", this.preFocus, this);
9269             
9270         }
9271         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9272             this.inputEl().on("keypress", this.filterKeys, this);
9273         } else {
9274             this.inputEl().relayEvent('keypress', this);
9275         }
9276        /* if(this.grow){
9277             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9278             this.el.on("click", this.autoSize,  this);
9279         }
9280         */
9281         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9282             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9283         }
9284         
9285         if (typeof(this.before) == 'object') {
9286             this.before.render(this.el.select('.roo-input-before',true).first());
9287         }
9288         if (typeof(this.after) == 'object') {
9289             this.after.render(this.el.select('.roo-input-after',true).first());
9290         }
9291         
9292         this.inputEl().on('change', this.onChange, this);
9293         
9294     },
9295     filterValidation : function(e){
9296         if(!e.isNavKeyPress()){
9297             this.validationTask.delay(this.validationDelay);
9298         }
9299     },
9300      /**
9301      * Validates the field value
9302      * @return {Boolean} True if the value is valid, else false
9303      */
9304     validate : function(){
9305         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9306         if(this.disabled || this.validateValue(this.getRawValue())){
9307             this.markValid();
9308             return true;
9309         }
9310         
9311         this.markInvalid();
9312         return false;
9313     },
9314     
9315     
9316     /**
9317      * Validates a value according to the field's validation rules and marks the field as invalid
9318      * if the validation fails
9319      * @param {Mixed} value The value to validate
9320      * @return {Boolean} True if the value is valid, else false
9321      */
9322     validateValue : function(value)
9323     {
9324         if(this.getVisibilityEl().hasClass('hidden')){
9325             return true;
9326         }
9327         
9328         if(value.length < 1)  { // if it's blank
9329             if(this.allowBlank){
9330                 return true;
9331             }
9332             return false;
9333         }
9334         
9335         if(value.length < this.minLength){
9336             return false;
9337         }
9338         if(value.length > this.maxLength){
9339             return false;
9340         }
9341         if(this.vtype){
9342             var vt = Roo.form.VTypes;
9343             if(!vt[this.vtype](value, this)){
9344                 return false;
9345             }
9346         }
9347         if(typeof this.validator == "function"){
9348             var msg = this.validator(value);
9349             if(msg !== true){
9350                 return false;
9351             }
9352             if (typeof(msg) == 'string') {
9353                 this.invalidText = msg;
9354             }
9355         }
9356         
9357         if(this.regex && !this.regex.test(value)){
9358             return false;
9359         }
9360         
9361         return true;
9362     },
9363     
9364      // private
9365     fireKey : function(e){
9366         //Roo.log('field ' + e.getKey());
9367         if(e.isNavKeyPress()){
9368             this.fireEvent("specialkey", this, e);
9369         }
9370     },
9371     focus : function (selectText){
9372         if(this.rendered){
9373             this.inputEl().focus();
9374             if(selectText === true){
9375                 this.inputEl().dom.select();
9376             }
9377         }
9378         return this;
9379     } ,
9380     
9381     onFocus : function(){
9382         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9383            // this.el.addClass(this.focusClass);
9384         }
9385         if(!this.hasFocus){
9386             this.hasFocus = true;
9387             this.startValue = this.getValue();
9388             this.fireEvent("focus", this);
9389         }
9390     },
9391     
9392     beforeBlur : Roo.emptyFn,
9393
9394     
9395     // private
9396     onBlur : function(){
9397         this.beforeBlur();
9398         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9399             //this.el.removeClass(this.focusClass);
9400         }
9401         this.hasFocus = false;
9402         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9403             this.validate();
9404         }
9405         var v = this.getValue();
9406         if(String(v) !== String(this.startValue)){
9407             this.fireEvent('change', this, v, this.startValue);
9408         }
9409         this.fireEvent("blur", this);
9410     },
9411     
9412     onChange : function(e)
9413     {
9414         var v = this.getValue();
9415         if(String(v) !== String(this.startValue)){
9416             this.fireEvent('change', this, v, this.startValue);
9417         }
9418         
9419     },
9420     
9421     /**
9422      * Resets the current field value to the originally loaded value and clears any validation messages
9423      */
9424     reset : function(){
9425         this.setValue(this.originalValue);
9426         this.validate();
9427     },
9428      /**
9429      * Returns the name of the field
9430      * @return {Mixed} name The name field
9431      */
9432     getName: function(){
9433         return this.name;
9434     },
9435      /**
9436      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9437      * @return {Mixed} value The field value
9438      */
9439     getValue : function(){
9440         
9441         var v = this.inputEl().getValue();
9442         
9443         return v;
9444     },
9445     /**
9446      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9447      * @return {Mixed} value The field value
9448      */
9449     getRawValue : function(){
9450         var v = this.inputEl().getValue();
9451         
9452         return v;
9453     },
9454     
9455     /**
9456      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9457      * @param {Mixed} value The value to set
9458      */
9459     setRawValue : function(v){
9460         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9461     },
9462     
9463     selectText : function(start, end){
9464         var v = this.getRawValue();
9465         if(v.length > 0){
9466             start = start === undefined ? 0 : start;
9467             end = end === undefined ? v.length : end;
9468             var d = this.inputEl().dom;
9469             if(d.setSelectionRange){
9470                 d.setSelectionRange(start, end);
9471             }else if(d.createTextRange){
9472                 var range = d.createTextRange();
9473                 range.moveStart("character", start);
9474                 range.moveEnd("character", v.length-end);
9475                 range.select();
9476             }
9477         }
9478     },
9479     
9480     /**
9481      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9482      * @param {Mixed} value The value to set
9483      */
9484     setValue : function(v){
9485         this.value = v;
9486         if(this.rendered){
9487             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9488             this.validate();
9489         }
9490     },
9491     
9492     /*
9493     processValue : function(value){
9494         if(this.stripCharsRe){
9495             var newValue = value.replace(this.stripCharsRe, '');
9496             if(newValue !== value){
9497                 this.setRawValue(newValue);
9498                 return newValue;
9499             }
9500         }
9501         return value;
9502     },
9503   */
9504     preFocus : function(){
9505         
9506         if(this.selectOnFocus){
9507             this.inputEl().dom.select();
9508         }
9509     },
9510     filterKeys : function(e){
9511         var k = e.getKey();
9512         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9513             return;
9514         }
9515         var c = e.getCharCode(), cc = String.fromCharCode(c);
9516         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9517             return;
9518         }
9519         if(!this.maskRe.test(cc)){
9520             e.stopEvent();
9521         }
9522     },
9523      /**
9524      * Clear any invalid styles/messages for this field
9525      */
9526     clearInvalid : function(){
9527         
9528         if(!this.el || this.preventMark){ // not rendered
9529             return;
9530         }
9531         
9532      
9533         this.el.removeClass(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);
9541             }
9542             
9543         }
9544         
9545         if(this.indicator){
9546             this.indicator.removeClass('visible');
9547             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9548         }
9549         
9550         this.fireEvent('valid', this);
9551     },
9552     
9553      /**
9554      * Mark this field as valid
9555      */
9556     markValid : function()
9557     {
9558         if(!this.el  || this.preventMark){ // not rendered...
9559             return;
9560         }
9561         
9562         this.el.removeClass([this.invalidClass, this.validClass]);
9563         
9564         var feedback = this.el.select('.form-control-feedback', true).first();
9565             
9566         if(feedback){
9567             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9568         }
9569         
9570         if(this.indicator){
9571             this.indicator.removeClass('visible');
9572             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9573         }
9574         
9575         if(this.disabled){
9576             return;
9577         }
9578         
9579         if(this.allowBlank && !this.getRawValue().length){
9580             return;
9581         }
9582         
9583         this.el.addClass(this.validClass);
9584         
9585         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9586             
9587             var feedback = this.el.select('.form-control-feedback', true).first();
9588             
9589             if(feedback){
9590                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9591                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9592             }
9593             
9594         }
9595         
9596         this.fireEvent('valid', this);
9597     },
9598     
9599      /**
9600      * Mark this field as invalid
9601      * @param {String} msg The validation message
9602      */
9603     markInvalid : function(msg)
9604     {
9605         if(!this.el  || this.preventMark){ // not rendered
9606             return;
9607         }
9608         
9609         this.el.removeClass([this.invalidClass, this.validClass]);
9610         
9611         var feedback = this.el.select('.form-control-feedback', true).first();
9612             
9613         if(feedback){
9614             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9615         }
9616
9617         if(this.disabled){
9618             return;
9619         }
9620         
9621         if(this.allowBlank && !this.getRawValue().length){
9622             return;
9623         }
9624         
9625         if(this.indicator){
9626             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9627             this.indicator.addClass('visible');
9628         }
9629         
9630         this.el.addClass(this.invalidClass);
9631         
9632         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9633             
9634             var feedback = this.el.select('.form-control-feedback', true).first();
9635             
9636             if(feedback){
9637                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9638                 
9639                 if(this.getValue().length || this.forceFeedback){
9640                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9641                 }
9642                 
9643             }
9644             
9645         }
9646         
9647         this.fireEvent('invalid', this, msg);
9648     },
9649     // private
9650     SafariOnKeyDown : function(event)
9651     {
9652         // this is a workaround for a password hang bug on chrome/ webkit.
9653         if (this.inputEl().dom.type != 'password') {
9654             return;
9655         }
9656         
9657         var isSelectAll = false;
9658         
9659         if(this.inputEl().dom.selectionEnd > 0){
9660             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9661         }
9662         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9663             event.preventDefault();
9664             this.setValue('');
9665             return;
9666         }
9667         
9668         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9669             
9670             event.preventDefault();
9671             // this is very hacky as keydown always get's upper case.
9672             //
9673             var cc = String.fromCharCode(event.getCharCode());
9674             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9675             
9676         }
9677     },
9678     adjustWidth : function(tag, w){
9679         tag = tag.toLowerCase();
9680         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9681             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9682                 if(tag == 'input'){
9683                     return w + 2;
9684                 }
9685                 if(tag == 'textarea'){
9686                     return w-2;
9687                 }
9688             }else if(Roo.isOpera){
9689                 if(tag == 'input'){
9690                     return w + 2;
9691                 }
9692                 if(tag == 'textarea'){
9693                     return w-2;
9694                 }
9695             }
9696         }
9697         return w;
9698     },
9699     
9700     setFieldLabel : function(v)
9701     {
9702         if(!this.rendered){
9703             return;
9704         }
9705         
9706         if(this.indicator){
9707             var ar = this.el.select('label > span',true);
9708             
9709             if (ar.elements.length) {
9710                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9711                 this.fieldLabel = v;
9712                 return;
9713             }
9714             
9715             var br = this.el.select('label',true);
9716             
9717             if(br.elements.length) {
9718                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9719                 this.fieldLabel = v;
9720                 return;
9721             }
9722             
9723             Roo.log('Cannot Found any of label > span || label in input');
9724             return;
9725         }
9726         
9727         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9728         this.fieldLabel = v;
9729         
9730         
9731     }
9732 });
9733
9734  
9735 /*
9736  * - LGPL
9737  *
9738  * Input
9739  * 
9740  */
9741
9742 /**
9743  * @class Roo.bootstrap.TextArea
9744  * @extends Roo.bootstrap.Input
9745  * Bootstrap TextArea class
9746  * @cfg {Number} cols Specifies the visible width of a text area
9747  * @cfg {Number} rows Specifies the visible number of lines in a text area
9748  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9749  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9750  * @cfg {string} html text
9751  * 
9752  * @constructor
9753  * Create a new TextArea
9754  * @param {Object} config The config object
9755  */
9756
9757 Roo.bootstrap.TextArea = function(config){
9758     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9759    
9760 };
9761
9762 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9763      
9764     cols : false,
9765     rows : 5,
9766     readOnly : false,
9767     warp : 'soft',
9768     resize : false,
9769     value: false,
9770     html: false,
9771     
9772     getAutoCreate : function(){
9773         
9774         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9775         
9776         var id = Roo.id();
9777         
9778         var cfg = {};
9779         
9780         if(this.inputType != 'hidden'){
9781             cfg.cls = 'form-group' //input-group
9782         }
9783         
9784         var input =  {
9785             tag: 'textarea',
9786             id : id,
9787             warp : this.warp,
9788             rows : this.rows,
9789             value : this.value || '',
9790             html: this.html || '',
9791             cls : 'form-control',
9792             placeholder : this.placeholder || '' 
9793             
9794         };
9795         
9796         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9797             input.maxLength = this.maxLength;
9798         }
9799         
9800         if(this.resize){
9801             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9802         }
9803         
9804         if(this.cols){
9805             input.cols = this.cols;
9806         }
9807         
9808         if (this.readOnly) {
9809             input.readonly = true;
9810         }
9811         
9812         if (this.name) {
9813             input.name = this.name;
9814         }
9815         
9816         if (this.size) {
9817             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9818         }
9819         
9820         var settings=this;
9821         ['xs','sm','md','lg'].map(function(size){
9822             if (settings[size]) {
9823                 cfg.cls += ' col-' + size + '-' + settings[size];
9824             }
9825         });
9826         
9827         var inputblock = input;
9828         
9829         if(this.hasFeedback && !this.allowBlank){
9830             
9831             var feedback = {
9832                 tag: 'span',
9833                 cls: 'glyphicon form-control-feedback'
9834             };
9835
9836             inputblock = {
9837                 cls : 'has-feedback',
9838                 cn :  [
9839                     input,
9840                     feedback
9841                 ] 
9842             };  
9843         }
9844         
9845         
9846         if (this.before || this.after) {
9847             
9848             inputblock = {
9849                 cls : 'input-group',
9850                 cn :  [] 
9851             };
9852             if (this.before) {
9853                 inputblock.cn.push({
9854                     tag :'span',
9855                     cls : 'input-group-addon',
9856                     html : this.before
9857                 });
9858             }
9859             
9860             inputblock.cn.push(input);
9861             
9862             if(this.hasFeedback && !this.allowBlank){
9863                 inputblock.cls += ' has-feedback';
9864                 inputblock.cn.push(feedback);
9865             }
9866             
9867             if (this.after) {
9868                 inputblock.cn.push({
9869                     tag :'span',
9870                     cls : 'input-group-addon',
9871                     html : this.after
9872                 });
9873             }
9874             
9875         }
9876         
9877         if (align ==='left' && this.fieldLabel.length) {
9878             cfg.cn = [
9879                 {
9880                     tag: 'label',
9881                     'for' :  id,
9882                     cls : 'control-label',
9883                     html : this.fieldLabel
9884                 },
9885                 {
9886                     cls : "",
9887                     cn: [
9888                         inputblock
9889                     ]
9890                 }
9891
9892             ];
9893             
9894             if(this.labelWidth > 12){
9895                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9896             }
9897
9898             if(this.labelWidth < 13 && this.labelmd == 0){
9899                 this.labelmd = this.labelWidth;
9900             }
9901
9902             if(this.labellg > 0){
9903                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9904                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9905             }
9906
9907             if(this.labelmd > 0){
9908                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9909                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9910             }
9911
9912             if(this.labelsm > 0){
9913                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9914                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9915             }
9916
9917             if(this.labelxs > 0){
9918                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9919                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9920             }
9921             
9922         } else if ( this.fieldLabel.length) {
9923             cfg.cn = [
9924
9925                {
9926                    tag: 'label',
9927                    //cls : 'input-group-addon',
9928                    html : this.fieldLabel
9929
9930                },
9931
9932                inputblock
9933
9934            ];
9935
9936         } else {
9937
9938             cfg.cn = [
9939
9940                 inputblock
9941
9942             ];
9943                 
9944         }
9945         
9946         if (this.disabled) {
9947             input.disabled=true;
9948         }
9949         
9950         return cfg;
9951         
9952     },
9953     /**
9954      * return the real textarea element.
9955      */
9956     inputEl: function ()
9957     {
9958         return this.el.select('textarea.form-control',true).first();
9959     },
9960     
9961     /**
9962      * Clear any invalid styles/messages for this field
9963      */
9964     clearInvalid : function()
9965     {
9966         
9967         if(!this.el || this.preventMark){ // not rendered
9968             return;
9969         }
9970         
9971         var label = this.el.select('label', true).first();
9972         var icon = this.el.select('i.fa-star', true).first();
9973         
9974         if(label && icon){
9975             icon.remove();
9976         }
9977         
9978         this.el.removeClass(this.invalidClass);
9979         
9980         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9981             
9982             var feedback = this.el.select('.form-control-feedback', true).first();
9983             
9984             if(feedback){
9985                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9986             }
9987             
9988         }
9989         
9990         this.fireEvent('valid', this);
9991     },
9992     
9993      /**
9994      * Mark this field as valid
9995      */
9996     markValid : function()
9997     {
9998         if(!this.el  || this.preventMark){ // not rendered
9999             return;
10000         }
10001         
10002         this.el.removeClass([this.invalidClass, this.validClass]);
10003         
10004         var feedback = this.el.select('.form-control-feedback', true).first();
10005             
10006         if(feedback){
10007             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10008         }
10009
10010         if(this.disabled || this.allowBlank){
10011             return;
10012         }
10013         
10014         var label = this.el.select('label', true).first();
10015         var icon = this.el.select('i.fa-star', true).first();
10016         
10017         if(label && icon){
10018             icon.remove();
10019         }
10020         
10021         this.el.addClass(this.validClass);
10022         
10023         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10024             
10025             var feedback = this.el.select('.form-control-feedback', true).first();
10026             
10027             if(feedback){
10028                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10029                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10030             }
10031             
10032         }
10033         
10034         this.fireEvent('valid', this);
10035     },
10036     
10037      /**
10038      * Mark this field as invalid
10039      * @param {String} msg The validation message
10040      */
10041     markInvalid : function(msg)
10042     {
10043         if(!this.el  || this.preventMark){ // not rendered
10044             return;
10045         }
10046         
10047         this.el.removeClass([this.invalidClass, this.validClass]);
10048         
10049         var feedback = this.el.select('.form-control-feedback', true).first();
10050             
10051         if(feedback){
10052             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10053         }
10054
10055         if(this.disabled || this.allowBlank){
10056             return;
10057         }
10058         
10059         var label = this.el.select('label', true).first();
10060         var icon = this.el.select('i.fa-star', true).first();
10061         
10062         if(!this.getValue().length && label && !icon){
10063             this.el.createChild({
10064                 tag : 'i',
10065                 cls : 'text-danger fa fa-lg fa-star',
10066                 tooltip : 'This field is required',
10067                 style : 'margin-right:5px;'
10068             }, label, true);
10069         }
10070
10071         this.el.addClass(this.invalidClass);
10072         
10073         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10074             
10075             var feedback = this.el.select('.form-control-feedback', true).first();
10076             
10077             if(feedback){
10078                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10079                 
10080                 if(this.getValue().length || this.forceFeedback){
10081                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10082                 }
10083                 
10084             }
10085             
10086         }
10087         
10088         this.fireEvent('invalid', this, msg);
10089     }
10090 });
10091
10092  
10093 /*
10094  * - LGPL
10095  *
10096  * trigger field - base class for combo..
10097  * 
10098  */
10099  
10100 /**
10101  * @class Roo.bootstrap.TriggerField
10102  * @extends Roo.bootstrap.Input
10103  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10104  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10105  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10106  * for which you can provide a custom implementation.  For example:
10107  * <pre><code>
10108 var trigger = new Roo.bootstrap.TriggerField();
10109 trigger.onTriggerClick = myTriggerFn;
10110 trigger.applyTo('my-field');
10111 </code></pre>
10112  *
10113  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10114  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10115  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10116  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10117  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10118
10119  * @constructor
10120  * Create a new TriggerField.
10121  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10122  * to the base TextField)
10123  */
10124 Roo.bootstrap.TriggerField = function(config){
10125     this.mimicing = false;
10126     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10127 };
10128
10129 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10130     /**
10131      * @cfg {String} triggerClass A CSS class to apply to the trigger
10132      */
10133      /**
10134      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10135      */
10136     hideTrigger:false,
10137
10138     /**
10139      * @cfg {Boolean} removable (true|false) special filter default false
10140      */
10141     removable : false,
10142     
10143     /** @cfg {Boolean} grow @hide */
10144     /** @cfg {Number} growMin @hide */
10145     /** @cfg {Number} growMax @hide */
10146
10147     /**
10148      * @hide 
10149      * @method
10150      */
10151     autoSize: Roo.emptyFn,
10152     // private
10153     monitorTab : true,
10154     // private
10155     deferHeight : true,
10156
10157     
10158     actionMode : 'wrap',
10159     
10160     caret : false,
10161     
10162     
10163     getAutoCreate : function(){
10164        
10165         var align = this.labelAlign || this.parentLabelAlign();
10166         
10167         var id = Roo.id();
10168         
10169         var cfg = {
10170             cls: 'form-group' //input-group
10171         };
10172         
10173         
10174         var input =  {
10175             tag: 'input',
10176             id : id,
10177             type : this.inputType,
10178             cls : 'form-control',
10179             autocomplete: 'new-password',
10180             placeholder : this.placeholder || '' 
10181             
10182         };
10183         if (this.name) {
10184             input.name = this.name;
10185         }
10186         if (this.size) {
10187             input.cls += ' input-' + this.size;
10188         }
10189         
10190         if (this.disabled) {
10191             input.disabled=true;
10192         }
10193         
10194         var inputblock = input;
10195         
10196         if(this.hasFeedback && !this.allowBlank){
10197             
10198             var feedback = {
10199                 tag: 'span',
10200                 cls: 'glyphicon form-control-feedback'
10201             };
10202             
10203             if(this.removable && !this.editable && !this.tickable){
10204                 inputblock = {
10205                     cls : 'has-feedback',
10206                     cn :  [
10207                         inputblock,
10208                         {
10209                             tag: 'button',
10210                             html : 'x',
10211                             cls : 'roo-combo-removable-btn close'
10212                         },
10213                         feedback
10214                     ] 
10215                 };
10216             } else {
10217                 inputblock = {
10218                     cls : 'has-feedback',
10219                     cn :  [
10220                         inputblock,
10221                         feedback
10222                     ] 
10223                 };
10224             }
10225
10226         } else {
10227             if(this.removable && !this.editable && !this.tickable){
10228                 inputblock = {
10229                     cls : 'roo-removable',
10230                     cn :  [
10231                         inputblock,
10232                         {
10233                             tag: 'button',
10234                             html : 'x',
10235                             cls : 'roo-combo-removable-btn close'
10236                         }
10237                     ] 
10238                 };
10239             }
10240         }
10241         
10242         if (this.before || this.after) {
10243             
10244             inputblock = {
10245                 cls : 'input-group',
10246                 cn :  [] 
10247             };
10248             if (this.before) {
10249                 inputblock.cn.push({
10250                     tag :'span',
10251                     cls : 'input-group-addon',
10252                     html : this.before
10253                 });
10254             }
10255             
10256             inputblock.cn.push(input);
10257             
10258             if(this.hasFeedback && !this.allowBlank){
10259                 inputblock.cls += ' has-feedback';
10260                 inputblock.cn.push(feedback);
10261             }
10262             
10263             if (this.after) {
10264                 inputblock.cn.push({
10265                     tag :'span',
10266                     cls : 'input-group-addon',
10267                     html : this.after
10268                 });
10269             }
10270             
10271         };
10272         
10273         var box = {
10274             tag: 'div',
10275             cn: [
10276                 {
10277                     tag: 'input',
10278                     type : 'hidden',
10279                     cls: 'form-hidden-field'
10280                 },
10281                 inputblock
10282             ]
10283             
10284         };
10285         
10286         if(this.multiple){
10287             box = {
10288                 tag: 'div',
10289                 cn: [
10290                     {
10291                         tag: 'input',
10292                         type : 'hidden',
10293                         cls: 'form-hidden-field'
10294                     },
10295                     {
10296                         tag: 'ul',
10297                         cls: 'roo-select2-choices',
10298                         cn:[
10299                             {
10300                                 tag: 'li',
10301                                 cls: 'roo-select2-search-field',
10302                                 cn: [
10303
10304                                     inputblock
10305                                 ]
10306                             }
10307                         ]
10308                     }
10309                 ]
10310             }
10311         };
10312         
10313         var combobox = {
10314             cls: 'roo-select2-container input-group',
10315             cn: [
10316                 box
10317 //                {
10318 //                    tag: 'ul',
10319 //                    cls: 'typeahead typeahead-long dropdown-menu',
10320 //                    style: 'display:none'
10321 //                }
10322             ]
10323         };
10324         
10325         if(!this.multiple && this.showToggleBtn){
10326             
10327             var caret = {
10328                         tag: 'span',
10329                         cls: 'caret'
10330              };
10331             if (this.caret != false) {
10332                 caret = {
10333                      tag: 'i',
10334                      cls: 'fa fa-' + this.caret
10335                 };
10336                 
10337             }
10338             
10339             combobox.cn.push({
10340                 tag :'span',
10341                 cls : 'input-group-addon btn dropdown-toggle',
10342                 cn : [
10343                     caret,
10344                     {
10345                         tag: 'span',
10346                         cls: 'combobox-clear',
10347                         cn  : [
10348                             {
10349                                 tag : 'i',
10350                                 cls: 'icon-remove'
10351                             }
10352                         ]
10353                     }
10354                 ]
10355
10356             })
10357         }
10358         
10359         if(this.multiple){
10360             combobox.cls += ' roo-select2-container-multi';
10361         }
10362         
10363         if (align ==='left' && this.fieldLabel.length) {
10364             
10365             cfg.cls += ' roo-form-group-label-left';
10366
10367             cfg.cn = [
10368                 {
10369                     tag : 'i',
10370                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10371                     tooltip : 'This field is required'
10372                 },
10373                 {
10374                     tag: 'label',
10375                     'for' :  id,
10376                     cls : 'control-label',
10377                     html : this.fieldLabel
10378
10379                 },
10380                 {
10381                     cls : "", 
10382                     cn: [
10383                         combobox
10384                     ]
10385                 }
10386
10387             ];
10388             
10389             var labelCfg = cfg.cn[1];
10390             var contentCfg = cfg.cn[2];
10391             
10392             if(this.indicatorpos == 'right'){
10393                 cfg.cn = [
10394                     {
10395                         tag: 'label',
10396                         'for' :  id,
10397                         cls : 'control-label',
10398                         cn : [
10399                             {
10400                                 tag : 'span',
10401                                 html : this.fieldLabel
10402                             },
10403                             {
10404                                 tag : 'i',
10405                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10406                                 tooltip : 'This field is required'
10407                             }
10408                         ]
10409                     },
10410                     {
10411                         cls : "", 
10412                         cn: [
10413                             combobox
10414                         ]
10415                     }
10416
10417                 ];
10418                 
10419                 labelCfg = cfg.cn[0];
10420                 contentCfg = cfg.cn[1];
10421             }
10422             
10423             if(this.labelWidth > 12){
10424                 labelCfg.style = "width: " + this.labelWidth + 'px';
10425             }
10426             
10427             if(this.labelWidth < 13 && this.labelmd == 0){
10428                 this.labelmd = this.labelWidth;
10429             }
10430             
10431             if(this.labellg > 0){
10432                 labelCfg.cls += ' col-lg-' + this.labellg;
10433                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10434             }
10435             
10436             if(this.labelmd > 0){
10437                 labelCfg.cls += ' col-md-' + this.labelmd;
10438                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10439             }
10440             
10441             if(this.labelsm > 0){
10442                 labelCfg.cls += ' col-sm-' + this.labelsm;
10443                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10444             }
10445             
10446             if(this.labelxs > 0){
10447                 labelCfg.cls += ' col-xs-' + this.labelxs;
10448                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10449             }
10450             
10451         } else if ( this.fieldLabel.length) {
10452 //                Roo.log(" label");
10453             cfg.cn = [
10454                 {
10455                    tag : 'i',
10456                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10457                    tooltip : 'This field is required'
10458                },
10459                {
10460                    tag: 'label',
10461                    //cls : 'input-group-addon',
10462                    html : this.fieldLabel
10463
10464                },
10465
10466                combobox
10467
10468             ];
10469             
10470             if(this.indicatorpos == 'right'){
10471                 
10472                 cfg.cn = [
10473                     {
10474                        tag: 'label',
10475                        cn : [
10476                            {
10477                                tag : 'span',
10478                                html : this.fieldLabel
10479                            },
10480                            {
10481                               tag : 'i',
10482                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10483                               tooltip : 'This field is required'
10484                            }
10485                        ]
10486
10487                     },
10488                     combobox
10489
10490                 ];
10491
10492             }
10493
10494         } else {
10495             
10496 //                Roo.log(" no label && no align");
10497                 cfg = combobox
10498                      
10499                 
10500         }
10501         
10502         var settings=this;
10503         ['xs','sm','md','lg'].map(function(size){
10504             if (settings[size]) {
10505                 cfg.cls += ' col-' + size + '-' + settings[size];
10506             }
10507         });
10508         
10509         return cfg;
10510         
10511     },
10512     
10513     
10514     
10515     // private
10516     onResize : function(w, h){
10517 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10518 //        if(typeof w == 'number'){
10519 //            var x = w - this.trigger.getWidth();
10520 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10521 //            this.trigger.setStyle('left', x+'px');
10522 //        }
10523     },
10524
10525     // private
10526     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10527
10528     // private
10529     getResizeEl : function(){
10530         return this.inputEl();
10531     },
10532
10533     // private
10534     getPositionEl : function(){
10535         return this.inputEl();
10536     },
10537
10538     // private
10539     alignErrorIcon : function(){
10540         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10541     },
10542
10543     // private
10544     initEvents : function(){
10545         
10546         this.createList();
10547         
10548         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10549         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10550         if(!this.multiple && this.showToggleBtn){
10551             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10552             if(this.hideTrigger){
10553                 this.trigger.setDisplayed(false);
10554             }
10555             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10556         }
10557         
10558         if(this.multiple){
10559             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10560         }
10561         
10562         if(this.removable && !this.editable && !this.tickable){
10563             var close = this.closeTriggerEl();
10564             
10565             if(close){
10566                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10567                 close.on('click', this.removeBtnClick, this, close);
10568             }
10569         }
10570         
10571         //this.trigger.addClassOnOver('x-form-trigger-over');
10572         //this.trigger.addClassOnClick('x-form-trigger-click');
10573         
10574         //if(!this.width){
10575         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10576         //}
10577     },
10578     
10579     closeTriggerEl : function()
10580     {
10581         var close = this.el.select('.roo-combo-removable-btn', true).first();
10582         return close ? close : false;
10583     },
10584     
10585     removeBtnClick : function(e, h, el)
10586     {
10587         e.preventDefault();
10588         
10589         if(this.fireEvent("remove", this) !== false){
10590             this.reset();
10591             this.fireEvent("afterremove", this)
10592         }
10593     },
10594     
10595     createList : function()
10596     {
10597         this.list = Roo.get(document.body).createChild({
10598             tag: 'ul',
10599             cls: 'typeahead typeahead-long dropdown-menu',
10600             style: 'display:none'
10601         });
10602         
10603         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10604         
10605     },
10606
10607     // private
10608     initTrigger : function(){
10609        
10610     },
10611
10612     // private
10613     onDestroy : function(){
10614         if(this.trigger){
10615             this.trigger.removeAllListeners();
10616           //  this.trigger.remove();
10617         }
10618         //if(this.wrap){
10619         //    this.wrap.remove();
10620         //}
10621         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10622     },
10623
10624     // private
10625     onFocus : function(){
10626         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10627         /*
10628         if(!this.mimicing){
10629             this.wrap.addClass('x-trigger-wrap-focus');
10630             this.mimicing = true;
10631             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10632             if(this.monitorTab){
10633                 this.el.on("keydown", this.checkTab, this);
10634             }
10635         }
10636         */
10637     },
10638
10639     // private
10640     checkTab : function(e){
10641         if(e.getKey() == e.TAB){
10642             this.triggerBlur();
10643         }
10644     },
10645
10646     // private
10647     onBlur : function(){
10648         // do nothing
10649     },
10650
10651     // private
10652     mimicBlur : function(e, t){
10653         /*
10654         if(!this.wrap.contains(t) && this.validateBlur()){
10655             this.triggerBlur();
10656         }
10657         */
10658     },
10659
10660     // private
10661     triggerBlur : function(){
10662         this.mimicing = false;
10663         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10664         if(this.monitorTab){
10665             this.el.un("keydown", this.checkTab, this);
10666         }
10667         //this.wrap.removeClass('x-trigger-wrap-focus');
10668         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10669     },
10670
10671     // private
10672     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10673     validateBlur : function(e, t){
10674         return true;
10675     },
10676
10677     // private
10678     onDisable : function(){
10679         this.inputEl().dom.disabled = true;
10680         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10681         //if(this.wrap){
10682         //    this.wrap.addClass('x-item-disabled');
10683         //}
10684     },
10685
10686     // private
10687     onEnable : function(){
10688         this.inputEl().dom.disabled = false;
10689         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10690         //if(this.wrap){
10691         //    this.el.removeClass('x-item-disabled');
10692         //}
10693     },
10694
10695     // private
10696     onShow : function(){
10697         var ae = this.getActionEl();
10698         
10699         if(ae){
10700             ae.dom.style.display = '';
10701             ae.dom.style.visibility = 'visible';
10702         }
10703     },
10704
10705     // private
10706     
10707     onHide : function(){
10708         var ae = this.getActionEl();
10709         ae.dom.style.display = 'none';
10710     },
10711
10712     /**
10713      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10714      * by an implementing function.
10715      * @method
10716      * @param {EventObject} e
10717      */
10718     onTriggerClick : Roo.emptyFn
10719 });
10720  /*
10721  * Based on:
10722  * Ext JS Library 1.1.1
10723  * Copyright(c) 2006-2007, Ext JS, LLC.
10724  *
10725  * Originally Released Under LGPL - original licence link has changed is not relivant.
10726  *
10727  * Fork - LGPL
10728  * <script type="text/javascript">
10729  */
10730
10731
10732 /**
10733  * @class Roo.data.SortTypes
10734  * @singleton
10735  * Defines the default sorting (casting?) comparison functions used when sorting data.
10736  */
10737 Roo.data.SortTypes = {
10738     /**
10739      * Default sort that does nothing
10740      * @param {Mixed} s The value being converted
10741      * @return {Mixed} The comparison value
10742      */
10743     none : function(s){
10744         return s;
10745     },
10746     
10747     /**
10748      * The regular expression used to strip tags
10749      * @type {RegExp}
10750      * @property
10751      */
10752     stripTagsRE : /<\/?[^>]+>/gi,
10753     
10754     /**
10755      * Strips all HTML tags to sort on text only
10756      * @param {Mixed} s The value being converted
10757      * @return {String} The comparison value
10758      */
10759     asText : function(s){
10760         return String(s).replace(this.stripTagsRE, "");
10761     },
10762     
10763     /**
10764      * Strips all HTML tags to sort on text only - Case insensitive
10765      * @param {Mixed} s The value being converted
10766      * @return {String} The comparison value
10767      */
10768     asUCText : function(s){
10769         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10770     },
10771     
10772     /**
10773      * Case insensitive string
10774      * @param {Mixed} s The value being converted
10775      * @return {String} The comparison value
10776      */
10777     asUCString : function(s) {
10778         return String(s).toUpperCase();
10779     },
10780     
10781     /**
10782      * Date sorting
10783      * @param {Mixed} s The value being converted
10784      * @return {Number} The comparison value
10785      */
10786     asDate : function(s) {
10787         if(!s){
10788             return 0;
10789         }
10790         if(s instanceof Date){
10791             return s.getTime();
10792         }
10793         return Date.parse(String(s));
10794     },
10795     
10796     /**
10797      * Float sorting
10798      * @param {Mixed} s The value being converted
10799      * @return {Float} The comparison value
10800      */
10801     asFloat : function(s) {
10802         var val = parseFloat(String(s).replace(/,/g, ""));
10803         if(isNaN(val)) {
10804             val = 0;
10805         }
10806         return val;
10807     },
10808     
10809     /**
10810      * Integer sorting
10811      * @param {Mixed} s The value being converted
10812      * @return {Number} The comparison value
10813      */
10814     asInt : function(s) {
10815         var val = parseInt(String(s).replace(/,/g, ""));
10816         if(isNaN(val)) {
10817             val = 0;
10818         }
10819         return val;
10820     }
10821 };/*
10822  * Based on:
10823  * Ext JS Library 1.1.1
10824  * Copyright(c) 2006-2007, Ext JS, LLC.
10825  *
10826  * Originally Released Under LGPL - original licence link has changed is not relivant.
10827  *
10828  * Fork - LGPL
10829  * <script type="text/javascript">
10830  */
10831
10832 /**
10833 * @class Roo.data.Record
10834  * Instances of this class encapsulate both record <em>definition</em> information, and record
10835  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10836  * to access Records cached in an {@link Roo.data.Store} object.<br>
10837  * <p>
10838  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10839  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10840  * objects.<br>
10841  * <p>
10842  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10843  * @constructor
10844  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10845  * {@link #create}. The parameters are the same.
10846  * @param {Array} data An associative Array of data values keyed by the field name.
10847  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10848  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10849  * not specified an integer id is generated.
10850  */
10851 Roo.data.Record = function(data, id){
10852     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10853     this.data = data;
10854 };
10855
10856 /**
10857  * Generate a constructor for a specific record layout.
10858  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10859  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10860  * Each field definition object may contain the following properties: <ul>
10861  * <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,
10862  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10863  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10864  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10865  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10866  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10867  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10868  * this may be omitted.</p></li>
10869  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10870  * <ul><li>auto (Default, implies no conversion)</li>
10871  * <li>string</li>
10872  * <li>int</li>
10873  * <li>float</li>
10874  * <li>boolean</li>
10875  * <li>date</li></ul></p></li>
10876  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10877  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10878  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10879  * by the Reader into an object that will be stored in the Record. It is passed the
10880  * following parameters:<ul>
10881  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10882  * </ul></p></li>
10883  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10884  * </ul>
10885  * <br>usage:<br><pre><code>
10886 var TopicRecord = Roo.data.Record.create(
10887     {name: 'title', mapping: 'topic_title'},
10888     {name: 'author', mapping: 'username'},
10889     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10890     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10891     {name: 'lastPoster', mapping: 'user2'},
10892     {name: 'excerpt', mapping: 'post_text'}
10893 );
10894
10895 var myNewRecord = new TopicRecord({
10896     title: 'Do my job please',
10897     author: 'noobie',
10898     totalPosts: 1,
10899     lastPost: new Date(),
10900     lastPoster: 'Animal',
10901     excerpt: 'No way dude!'
10902 });
10903 myStore.add(myNewRecord);
10904 </code></pre>
10905  * @method create
10906  * @static
10907  */
10908 Roo.data.Record.create = function(o){
10909     var f = function(){
10910         f.superclass.constructor.apply(this, arguments);
10911     };
10912     Roo.extend(f, Roo.data.Record);
10913     var p = f.prototype;
10914     p.fields = new Roo.util.MixedCollection(false, function(field){
10915         return field.name;
10916     });
10917     for(var i = 0, len = o.length; i < len; i++){
10918         p.fields.add(new Roo.data.Field(o[i]));
10919     }
10920     f.getField = function(name){
10921         return p.fields.get(name);  
10922     };
10923     return f;
10924 };
10925
10926 Roo.data.Record.AUTO_ID = 1000;
10927 Roo.data.Record.EDIT = 'edit';
10928 Roo.data.Record.REJECT = 'reject';
10929 Roo.data.Record.COMMIT = 'commit';
10930
10931 Roo.data.Record.prototype = {
10932     /**
10933      * Readonly flag - true if this record has been modified.
10934      * @type Boolean
10935      */
10936     dirty : false,
10937     editing : false,
10938     error: null,
10939     modified: null,
10940
10941     // private
10942     join : function(store){
10943         this.store = store;
10944     },
10945
10946     /**
10947      * Set the named field to the specified value.
10948      * @param {String} name The name of the field to set.
10949      * @param {Object} value The value to set the field to.
10950      */
10951     set : function(name, value){
10952         if(this.data[name] == value){
10953             return;
10954         }
10955         this.dirty = true;
10956         if(!this.modified){
10957             this.modified = {};
10958         }
10959         if(typeof this.modified[name] == 'undefined'){
10960             this.modified[name] = this.data[name];
10961         }
10962         this.data[name] = value;
10963         if(!this.editing && this.store){
10964             this.store.afterEdit(this);
10965         }       
10966     },
10967
10968     /**
10969      * Get the value of the named field.
10970      * @param {String} name The name of the field to get the value of.
10971      * @return {Object} The value of the field.
10972      */
10973     get : function(name){
10974         return this.data[name]; 
10975     },
10976
10977     // private
10978     beginEdit : function(){
10979         this.editing = true;
10980         this.modified = {}; 
10981     },
10982
10983     // private
10984     cancelEdit : function(){
10985         this.editing = false;
10986         delete this.modified;
10987     },
10988
10989     // private
10990     endEdit : function(){
10991         this.editing = false;
10992         if(this.dirty && this.store){
10993             this.store.afterEdit(this);
10994         }
10995     },
10996
10997     /**
10998      * Usually called by the {@link Roo.data.Store} which owns the Record.
10999      * Rejects all changes made to the Record since either creation, or the last commit operation.
11000      * Modified fields are reverted to their original values.
11001      * <p>
11002      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11003      * of reject operations.
11004      */
11005     reject : function(){
11006         var m = this.modified;
11007         for(var n in m){
11008             if(typeof m[n] != "function"){
11009                 this.data[n] = m[n];
11010             }
11011         }
11012         this.dirty = false;
11013         delete this.modified;
11014         this.editing = false;
11015         if(this.store){
11016             this.store.afterReject(this);
11017         }
11018     },
11019
11020     /**
11021      * Usually called by the {@link Roo.data.Store} which owns the Record.
11022      * Commits all changes made to the Record since either creation, or the last commit operation.
11023      * <p>
11024      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11025      * of commit operations.
11026      */
11027     commit : function(){
11028         this.dirty = false;
11029         delete this.modified;
11030         this.editing = false;
11031         if(this.store){
11032             this.store.afterCommit(this);
11033         }
11034     },
11035
11036     // private
11037     hasError : function(){
11038         return this.error != null;
11039     },
11040
11041     // private
11042     clearError : function(){
11043         this.error = null;
11044     },
11045
11046     /**
11047      * Creates a copy of this record.
11048      * @param {String} id (optional) A new record id if you don't want to use this record's id
11049      * @return {Record}
11050      */
11051     copy : function(newId) {
11052         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11053     }
11054 };/*
11055  * Based on:
11056  * Ext JS Library 1.1.1
11057  * Copyright(c) 2006-2007, Ext JS, LLC.
11058  *
11059  * Originally Released Under LGPL - original licence link has changed is not relivant.
11060  *
11061  * Fork - LGPL
11062  * <script type="text/javascript">
11063  */
11064
11065
11066
11067 /**
11068  * @class Roo.data.Store
11069  * @extends Roo.util.Observable
11070  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11071  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11072  * <p>
11073  * 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
11074  * has no knowledge of the format of the data returned by the Proxy.<br>
11075  * <p>
11076  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11077  * instances from the data object. These records are cached and made available through accessor functions.
11078  * @constructor
11079  * Creates a new Store.
11080  * @param {Object} config A config object containing the objects needed for the Store to access data,
11081  * and read the data into Records.
11082  */
11083 Roo.data.Store = function(config){
11084     this.data = new Roo.util.MixedCollection(false);
11085     this.data.getKey = function(o){
11086         return o.id;
11087     };
11088     this.baseParams = {};
11089     // private
11090     this.paramNames = {
11091         "start" : "start",
11092         "limit" : "limit",
11093         "sort" : "sort",
11094         "dir" : "dir",
11095         "multisort" : "_multisort"
11096     };
11097
11098     if(config && config.data){
11099         this.inlineData = config.data;
11100         delete config.data;
11101     }
11102
11103     Roo.apply(this, config);
11104     
11105     if(this.reader){ // reader passed
11106         this.reader = Roo.factory(this.reader, Roo.data);
11107         this.reader.xmodule = this.xmodule || false;
11108         if(!this.recordType){
11109             this.recordType = this.reader.recordType;
11110         }
11111         if(this.reader.onMetaChange){
11112             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11113         }
11114     }
11115
11116     if(this.recordType){
11117         this.fields = this.recordType.prototype.fields;
11118     }
11119     this.modified = [];
11120
11121     this.addEvents({
11122         /**
11123          * @event datachanged
11124          * Fires when the data cache has changed, and a widget which is using this Store
11125          * as a Record cache should refresh its view.
11126          * @param {Store} this
11127          */
11128         datachanged : true,
11129         /**
11130          * @event metachange
11131          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11132          * @param {Store} this
11133          * @param {Object} meta The JSON metadata
11134          */
11135         metachange : true,
11136         /**
11137          * @event add
11138          * Fires when Records have been added to the Store
11139          * @param {Store} this
11140          * @param {Roo.data.Record[]} records The array of Records added
11141          * @param {Number} index The index at which the record(s) were added
11142          */
11143         add : true,
11144         /**
11145          * @event remove
11146          * Fires when a Record has been removed from the Store
11147          * @param {Store} this
11148          * @param {Roo.data.Record} record The Record that was removed
11149          * @param {Number} index The index at which the record was removed
11150          */
11151         remove : true,
11152         /**
11153          * @event update
11154          * Fires when a Record has been updated
11155          * @param {Store} this
11156          * @param {Roo.data.Record} record The Record that was updated
11157          * @param {String} operation The update operation being performed.  Value may be one of:
11158          * <pre><code>
11159  Roo.data.Record.EDIT
11160  Roo.data.Record.REJECT
11161  Roo.data.Record.COMMIT
11162          * </code></pre>
11163          */
11164         update : true,
11165         /**
11166          * @event clear
11167          * Fires when the data cache has been cleared.
11168          * @param {Store} this
11169          */
11170         clear : true,
11171         /**
11172          * @event beforeload
11173          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11174          * the load action will be canceled.
11175          * @param {Store} this
11176          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11177          */
11178         beforeload : true,
11179         /**
11180          * @event beforeloadadd
11181          * Fires after a new set of Records has been loaded.
11182          * @param {Store} this
11183          * @param {Roo.data.Record[]} records The Records that were loaded
11184          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11185          */
11186         beforeloadadd : true,
11187         /**
11188          * @event load
11189          * Fires after a new set of Records has been loaded, before they are added to the store.
11190          * @param {Store} this
11191          * @param {Roo.data.Record[]} records The Records that were loaded
11192          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11193          * @params {Object} return from reader
11194          */
11195         load : true,
11196         /**
11197          * @event loadexception
11198          * Fires if an exception occurs in the Proxy during loading.
11199          * Called with the signature of the Proxy's "loadexception" event.
11200          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11201          * 
11202          * @param {Proxy} 
11203          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11204          * @param {Object} load options 
11205          * @param {Object} jsonData from your request (normally this contains the Exception)
11206          */
11207         loadexception : true
11208     });
11209     
11210     if(this.proxy){
11211         this.proxy = Roo.factory(this.proxy, Roo.data);
11212         this.proxy.xmodule = this.xmodule || false;
11213         this.relayEvents(this.proxy,  ["loadexception"]);
11214     }
11215     this.sortToggle = {};
11216     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11217
11218     Roo.data.Store.superclass.constructor.call(this);
11219
11220     if(this.inlineData){
11221         this.loadData(this.inlineData);
11222         delete this.inlineData;
11223     }
11224 };
11225
11226 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11227      /**
11228     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11229     * without a remote query - used by combo/forms at present.
11230     */
11231     
11232     /**
11233     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11234     */
11235     /**
11236     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11237     */
11238     /**
11239     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11240     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11241     */
11242     /**
11243     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11244     * on any HTTP request
11245     */
11246     /**
11247     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11248     */
11249     /**
11250     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11251     */
11252     multiSort: false,
11253     /**
11254     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11255     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11256     */
11257     remoteSort : false,
11258
11259     /**
11260     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11261      * loaded or when a record is removed. (defaults to false).
11262     */
11263     pruneModifiedRecords : false,
11264
11265     // private
11266     lastOptions : null,
11267
11268     /**
11269      * Add Records to the Store and fires the add event.
11270      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11271      */
11272     add : function(records){
11273         records = [].concat(records);
11274         for(var i = 0, len = records.length; i < len; i++){
11275             records[i].join(this);
11276         }
11277         var index = this.data.length;
11278         this.data.addAll(records);
11279         this.fireEvent("add", this, records, index);
11280     },
11281
11282     /**
11283      * Remove a Record from the Store and fires the remove event.
11284      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11285      */
11286     remove : function(record){
11287         var index = this.data.indexOf(record);
11288         this.data.removeAt(index);
11289  
11290         if(this.pruneModifiedRecords){
11291             this.modified.remove(record);
11292         }
11293         this.fireEvent("remove", this, record, index);
11294     },
11295
11296     /**
11297      * Remove all Records from the Store and fires the clear event.
11298      */
11299     removeAll : function(){
11300         this.data.clear();
11301         if(this.pruneModifiedRecords){
11302             this.modified = [];
11303         }
11304         this.fireEvent("clear", this);
11305     },
11306
11307     /**
11308      * Inserts Records to the Store at the given index and fires the add event.
11309      * @param {Number} index The start index at which to insert the passed Records.
11310      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11311      */
11312     insert : function(index, records){
11313         records = [].concat(records);
11314         for(var i = 0, len = records.length; i < len; i++){
11315             this.data.insert(index, records[i]);
11316             records[i].join(this);
11317         }
11318         this.fireEvent("add", this, records, index);
11319     },
11320
11321     /**
11322      * Get the index within the cache of the passed Record.
11323      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11324      * @return {Number} The index of the passed Record. Returns -1 if not found.
11325      */
11326     indexOf : function(record){
11327         return this.data.indexOf(record);
11328     },
11329
11330     /**
11331      * Get the index within the cache of the Record with the passed id.
11332      * @param {String} id The id of the Record to find.
11333      * @return {Number} The index of the Record. Returns -1 if not found.
11334      */
11335     indexOfId : function(id){
11336         return this.data.indexOfKey(id);
11337     },
11338
11339     /**
11340      * Get the Record with the specified id.
11341      * @param {String} id The id of the Record to find.
11342      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11343      */
11344     getById : function(id){
11345         return this.data.key(id);
11346     },
11347
11348     /**
11349      * Get the Record at the specified index.
11350      * @param {Number} index The index of the Record to find.
11351      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11352      */
11353     getAt : function(index){
11354         return this.data.itemAt(index);
11355     },
11356
11357     /**
11358      * Returns a range of Records between specified indices.
11359      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11360      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11361      * @return {Roo.data.Record[]} An array of Records
11362      */
11363     getRange : function(start, end){
11364         return this.data.getRange(start, end);
11365     },
11366
11367     // private
11368     storeOptions : function(o){
11369         o = Roo.apply({}, o);
11370         delete o.callback;
11371         delete o.scope;
11372         this.lastOptions = o;
11373     },
11374
11375     /**
11376      * Loads the Record cache from the configured Proxy using the configured Reader.
11377      * <p>
11378      * If using remote paging, then the first load call must specify the <em>start</em>
11379      * and <em>limit</em> properties in the options.params property to establish the initial
11380      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11381      * <p>
11382      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11383      * and this call will return before the new data has been loaded. Perform any post-processing
11384      * in a callback function, or in a "load" event handler.</strong>
11385      * <p>
11386      * @param {Object} options An object containing properties which control loading options:<ul>
11387      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11388      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11389      * passed the following arguments:<ul>
11390      * <li>r : Roo.data.Record[]</li>
11391      * <li>options: Options object from the load call</li>
11392      * <li>success: Boolean success indicator</li></ul></li>
11393      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11394      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11395      * </ul>
11396      */
11397     load : function(options){
11398         options = options || {};
11399         if(this.fireEvent("beforeload", this, options) !== false){
11400             this.storeOptions(options);
11401             var p = Roo.apply(options.params || {}, this.baseParams);
11402             // if meta was not loaded from remote source.. try requesting it.
11403             if (!this.reader.metaFromRemote) {
11404                 p._requestMeta = 1;
11405             }
11406             if(this.sortInfo && this.remoteSort){
11407                 var pn = this.paramNames;
11408                 p[pn["sort"]] = this.sortInfo.field;
11409                 p[pn["dir"]] = this.sortInfo.direction;
11410             }
11411             if (this.multiSort) {
11412                 var pn = this.paramNames;
11413                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11414             }
11415             
11416             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11417         }
11418     },
11419
11420     /**
11421      * Reloads the Record cache from the configured Proxy using the configured Reader and
11422      * the options from the last load operation performed.
11423      * @param {Object} options (optional) An object containing properties which may override the options
11424      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11425      * the most recently used options are reused).
11426      */
11427     reload : function(options){
11428         this.load(Roo.applyIf(options||{}, this.lastOptions));
11429     },
11430
11431     // private
11432     // Called as a callback by the Reader during a load operation.
11433     loadRecords : function(o, options, success){
11434         if(!o || success === false){
11435             if(success !== false){
11436                 this.fireEvent("load", this, [], options, o);
11437             }
11438             if(options.callback){
11439                 options.callback.call(options.scope || this, [], options, false);
11440             }
11441             return;
11442         }
11443         // if data returned failure - throw an exception.
11444         if (o.success === false) {
11445             // show a message if no listener is registered.
11446             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11447                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11448             }
11449             // loadmask wil be hooked into this..
11450             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11451             return;
11452         }
11453         var r = o.records, t = o.totalRecords || r.length;
11454         
11455         this.fireEvent("beforeloadadd", this, r, options, o);
11456         
11457         if(!options || options.add !== true){
11458             if(this.pruneModifiedRecords){
11459                 this.modified = [];
11460             }
11461             for(var i = 0, len = r.length; i < len; i++){
11462                 r[i].join(this);
11463             }
11464             if(this.snapshot){
11465                 this.data = this.snapshot;
11466                 delete this.snapshot;
11467             }
11468             this.data.clear();
11469             this.data.addAll(r);
11470             this.totalLength = t;
11471             this.applySort();
11472             this.fireEvent("datachanged", this);
11473         }else{
11474             this.totalLength = Math.max(t, this.data.length+r.length);
11475             this.add(r);
11476         }
11477         
11478         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11479                 
11480             var e = new Roo.data.Record({});
11481
11482             e.set(this.parent.displayField, this.parent.emptyTitle);
11483             e.set(this.parent.valueField, '');
11484
11485             this.insert(0, e);
11486         }
11487             
11488         this.fireEvent("load", this, r, options, o);
11489         if(options.callback){
11490             options.callback.call(options.scope || this, r, options, true);
11491         }
11492     },
11493
11494
11495     /**
11496      * Loads data from a passed data block. A Reader which understands the format of the data
11497      * must have been configured in the constructor.
11498      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11499      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11500      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11501      */
11502     loadData : function(o, append){
11503         var r = this.reader.readRecords(o);
11504         this.loadRecords(r, {add: append}, true);
11505     },
11506
11507     /**
11508      * Gets the number of cached records.
11509      * <p>
11510      * <em>If using paging, this may not be the total size of the dataset. If the data object
11511      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11512      * the data set size</em>
11513      */
11514     getCount : function(){
11515         return this.data.length || 0;
11516     },
11517
11518     /**
11519      * Gets the total number of records in the dataset as returned by the server.
11520      * <p>
11521      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11522      * the dataset size</em>
11523      */
11524     getTotalCount : function(){
11525         return this.totalLength || 0;
11526     },
11527
11528     /**
11529      * Returns the sort state of the Store as an object with two properties:
11530      * <pre><code>
11531  field {String} The name of the field by which the Records are sorted
11532  direction {String} The sort order, "ASC" or "DESC"
11533      * </code></pre>
11534      */
11535     getSortState : function(){
11536         return this.sortInfo;
11537     },
11538
11539     // private
11540     applySort : function(){
11541         if(this.sortInfo && !this.remoteSort){
11542             var s = this.sortInfo, f = s.field;
11543             var st = this.fields.get(f).sortType;
11544             var fn = function(r1, r2){
11545                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11546                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11547             };
11548             this.data.sort(s.direction, fn);
11549             if(this.snapshot && this.snapshot != this.data){
11550                 this.snapshot.sort(s.direction, fn);
11551             }
11552         }
11553     },
11554
11555     /**
11556      * Sets the default sort column and order to be used by the next load operation.
11557      * @param {String} fieldName The name of the field to sort by.
11558      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11559      */
11560     setDefaultSort : function(field, dir){
11561         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11562     },
11563
11564     /**
11565      * Sort the Records.
11566      * If remote sorting is used, the sort is performed on the server, and the cache is
11567      * reloaded. If local sorting is used, the cache is sorted internally.
11568      * @param {String} fieldName The name of the field to sort by.
11569      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11570      */
11571     sort : function(fieldName, dir){
11572         var f = this.fields.get(fieldName);
11573         if(!dir){
11574             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11575             
11576             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11577                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11578             }else{
11579                 dir = f.sortDir;
11580             }
11581         }
11582         this.sortToggle[f.name] = dir;
11583         this.sortInfo = {field: f.name, direction: dir};
11584         if(!this.remoteSort){
11585             this.applySort();
11586             this.fireEvent("datachanged", this);
11587         }else{
11588             this.load(this.lastOptions);
11589         }
11590     },
11591
11592     /**
11593      * Calls the specified function for each of the Records in the cache.
11594      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11595      * Returning <em>false</em> aborts and exits the iteration.
11596      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11597      */
11598     each : function(fn, scope){
11599         this.data.each(fn, scope);
11600     },
11601
11602     /**
11603      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11604      * (e.g., during paging).
11605      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11606      */
11607     getModifiedRecords : function(){
11608         return this.modified;
11609     },
11610
11611     // private
11612     createFilterFn : function(property, value, anyMatch){
11613         if(!value.exec){ // not a regex
11614             value = String(value);
11615             if(value.length == 0){
11616                 return false;
11617             }
11618             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11619         }
11620         return function(r){
11621             return value.test(r.data[property]);
11622         };
11623     },
11624
11625     /**
11626      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11627      * @param {String} property A field on your records
11628      * @param {Number} start The record index to start at (defaults to 0)
11629      * @param {Number} end The last record index to include (defaults to length - 1)
11630      * @return {Number} The sum
11631      */
11632     sum : function(property, start, end){
11633         var rs = this.data.items, v = 0;
11634         start = start || 0;
11635         end = (end || end === 0) ? end : rs.length-1;
11636
11637         for(var i = start; i <= end; i++){
11638             v += (rs[i].data[property] || 0);
11639         }
11640         return v;
11641     },
11642
11643     /**
11644      * Filter the records by a specified property.
11645      * @param {String} field A field on your records
11646      * @param {String/RegExp} value Either a string that the field
11647      * should start with or a RegExp to test against the field
11648      * @param {Boolean} anyMatch True to match any part not just the beginning
11649      */
11650     filter : function(property, value, anyMatch){
11651         var fn = this.createFilterFn(property, value, anyMatch);
11652         return fn ? this.filterBy(fn) : this.clearFilter();
11653     },
11654
11655     /**
11656      * Filter by a function. The specified function will be called with each
11657      * record in this data source. If the function returns true the record is included,
11658      * otherwise it is filtered.
11659      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11660      * @param {Object} scope (optional) The scope of the function (defaults to this)
11661      */
11662     filterBy : function(fn, scope){
11663         this.snapshot = this.snapshot || this.data;
11664         this.data = this.queryBy(fn, scope||this);
11665         this.fireEvent("datachanged", this);
11666     },
11667
11668     /**
11669      * Query the records by a specified property.
11670      * @param {String} field A field on your records
11671      * @param {String/RegExp} value Either a string that the field
11672      * should start with or a RegExp to test against the field
11673      * @param {Boolean} anyMatch True to match any part not just the beginning
11674      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11675      */
11676     query : function(property, value, anyMatch){
11677         var fn = this.createFilterFn(property, value, anyMatch);
11678         return fn ? this.queryBy(fn) : this.data.clone();
11679     },
11680
11681     /**
11682      * Query by a function. The specified function will be called with each
11683      * record in this data source. If the function returns true the record is included
11684      * in the results.
11685      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11686      * @param {Object} scope (optional) The scope of the function (defaults to this)
11687       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11688      **/
11689     queryBy : function(fn, scope){
11690         var data = this.snapshot || this.data;
11691         return data.filterBy(fn, scope||this);
11692     },
11693
11694     /**
11695      * Collects unique values for a particular dataIndex from this store.
11696      * @param {String} dataIndex The property to collect
11697      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11698      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11699      * @return {Array} An array of the unique values
11700      **/
11701     collect : function(dataIndex, allowNull, bypassFilter){
11702         var d = (bypassFilter === true && this.snapshot) ?
11703                 this.snapshot.items : this.data.items;
11704         var v, sv, r = [], l = {};
11705         for(var i = 0, len = d.length; i < len; i++){
11706             v = d[i].data[dataIndex];
11707             sv = String(v);
11708             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11709                 l[sv] = true;
11710                 r[r.length] = v;
11711             }
11712         }
11713         return r;
11714     },
11715
11716     /**
11717      * Revert to a view of the Record cache with no filtering applied.
11718      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11719      */
11720     clearFilter : function(suppressEvent){
11721         if(this.snapshot && this.snapshot != this.data){
11722             this.data = this.snapshot;
11723             delete this.snapshot;
11724             if(suppressEvent !== true){
11725                 this.fireEvent("datachanged", this);
11726             }
11727         }
11728     },
11729
11730     // private
11731     afterEdit : function(record){
11732         if(this.modified.indexOf(record) == -1){
11733             this.modified.push(record);
11734         }
11735         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11736     },
11737     
11738     // private
11739     afterReject : function(record){
11740         this.modified.remove(record);
11741         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11742     },
11743
11744     // private
11745     afterCommit : function(record){
11746         this.modified.remove(record);
11747         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11748     },
11749
11750     /**
11751      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11752      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11753      */
11754     commitChanges : function(){
11755         var m = this.modified.slice(0);
11756         this.modified = [];
11757         for(var i = 0, len = m.length; i < len; i++){
11758             m[i].commit();
11759         }
11760     },
11761
11762     /**
11763      * Cancel outstanding changes on all changed records.
11764      */
11765     rejectChanges : function(){
11766         var m = this.modified.slice(0);
11767         this.modified = [];
11768         for(var i = 0, len = m.length; i < len; i++){
11769             m[i].reject();
11770         }
11771     },
11772
11773     onMetaChange : function(meta, rtype, o){
11774         this.recordType = rtype;
11775         this.fields = rtype.prototype.fields;
11776         delete this.snapshot;
11777         this.sortInfo = meta.sortInfo || this.sortInfo;
11778         this.modified = [];
11779         this.fireEvent('metachange', this, this.reader.meta);
11780     },
11781     
11782     moveIndex : function(data, type)
11783     {
11784         var index = this.indexOf(data);
11785         
11786         var newIndex = index + type;
11787         
11788         this.remove(data);
11789         
11790         this.insert(newIndex, data);
11791         
11792     }
11793 });/*
11794  * Based on:
11795  * Ext JS Library 1.1.1
11796  * Copyright(c) 2006-2007, Ext JS, LLC.
11797  *
11798  * Originally Released Under LGPL - original licence link has changed is not relivant.
11799  *
11800  * Fork - LGPL
11801  * <script type="text/javascript">
11802  */
11803
11804 /**
11805  * @class Roo.data.SimpleStore
11806  * @extends Roo.data.Store
11807  * Small helper class to make creating Stores from Array data easier.
11808  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11809  * @cfg {Array} fields An array of field definition objects, or field name strings.
11810  * @cfg {Array} data The multi-dimensional array of data
11811  * @constructor
11812  * @param {Object} config
11813  */
11814 Roo.data.SimpleStore = function(config){
11815     Roo.data.SimpleStore.superclass.constructor.call(this, {
11816         isLocal : true,
11817         reader: new Roo.data.ArrayReader({
11818                 id: config.id
11819             },
11820             Roo.data.Record.create(config.fields)
11821         ),
11822         proxy : new Roo.data.MemoryProxy(config.data)
11823     });
11824     this.load();
11825 };
11826 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11827  * Based on:
11828  * Ext JS Library 1.1.1
11829  * Copyright(c) 2006-2007, Ext JS, LLC.
11830  *
11831  * Originally Released Under LGPL - original licence link has changed is not relivant.
11832  *
11833  * Fork - LGPL
11834  * <script type="text/javascript">
11835  */
11836
11837 /**
11838 /**
11839  * @extends Roo.data.Store
11840  * @class Roo.data.JsonStore
11841  * Small helper class to make creating Stores for JSON data easier. <br/>
11842 <pre><code>
11843 var store = new Roo.data.JsonStore({
11844     url: 'get-images.php',
11845     root: 'images',
11846     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11847 });
11848 </code></pre>
11849  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11850  * JsonReader and HttpProxy (unless inline data is provided).</b>
11851  * @cfg {Array} fields An array of field definition objects, or field name strings.
11852  * @constructor
11853  * @param {Object} config
11854  */
11855 Roo.data.JsonStore = function(c){
11856     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11857         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11858         reader: new Roo.data.JsonReader(c, c.fields)
11859     }));
11860 };
11861 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11862  * Based on:
11863  * Ext JS Library 1.1.1
11864  * Copyright(c) 2006-2007, Ext JS, LLC.
11865  *
11866  * Originally Released Under LGPL - original licence link has changed is not relivant.
11867  *
11868  * Fork - LGPL
11869  * <script type="text/javascript">
11870  */
11871
11872  
11873 Roo.data.Field = function(config){
11874     if(typeof config == "string"){
11875         config = {name: config};
11876     }
11877     Roo.apply(this, config);
11878     
11879     if(!this.type){
11880         this.type = "auto";
11881     }
11882     
11883     var st = Roo.data.SortTypes;
11884     // named sortTypes are supported, here we look them up
11885     if(typeof this.sortType == "string"){
11886         this.sortType = st[this.sortType];
11887     }
11888     
11889     // set default sortType for strings and dates
11890     if(!this.sortType){
11891         switch(this.type){
11892             case "string":
11893                 this.sortType = st.asUCString;
11894                 break;
11895             case "date":
11896                 this.sortType = st.asDate;
11897                 break;
11898             default:
11899                 this.sortType = st.none;
11900         }
11901     }
11902
11903     // define once
11904     var stripRe = /[\$,%]/g;
11905
11906     // prebuilt conversion function for this field, instead of
11907     // switching every time we're reading a value
11908     if(!this.convert){
11909         var cv, dateFormat = this.dateFormat;
11910         switch(this.type){
11911             case "":
11912             case "auto":
11913             case undefined:
11914                 cv = function(v){ return v; };
11915                 break;
11916             case "string":
11917                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11918                 break;
11919             case "int":
11920                 cv = function(v){
11921                     return v !== undefined && v !== null && v !== '' ?
11922                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11923                     };
11924                 break;
11925             case "float":
11926                 cv = function(v){
11927                     return v !== undefined && v !== null && v !== '' ?
11928                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11929                     };
11930                 break;
11931             case "bool":
11932             case "boolean":
11933                 cv = function(v){ return v === true || v === "true" || v == 1; };
11934                 break;
11935             case "date":
11936                 cv = function(v){
11937                     if(!v){
11938                         return '';
11939                     }
11940                     if(v instanceof Date){
11941                         return v;
11942                     }
11943                     if(dateFormat){
11944                         if(dateFormat == "timestamp"){
11945                             return new Date(v*1000);
11946                         }
11947                         return Date.parseDate(v, dateFormat);
11948                     }
11949                     var parsed = Date.parse(v);
11950                     return parsed ? new Date(parsed) : null;
11951                 };
11952              break;
11953             
11954         }
11955         this.convert = cv;
11956     }
11957 };
11958
11959 Roo.data.Field.prototype = {
11960     dateFormat: null,
11961     defaultValue: "",
11962     mapping: null,
11963     sortType : null,
11964     sortDir : "ASC"
11965 };/*
11966  * Based on:
11967  * Ext JS Library 1.1.1
11968  * Copyright(c) 2006-2007, Ext JS, LLC.
11969  *
11970  * Originally Released Under LGPL - original licence link has changed is not relivant.
11971  *
11972  * Fork - LGPL
11973  * <script type="text/javascript">
11974  */
11975  
11976 // Base class for reading structured data from a data source.  This class is intended to be
11977 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11978
11979 /**
11980  * @class Roo.data.DataReader
11981  * Base class for reading structured data from a data source.  This class is intended to be
11982  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11983  */
11984
11985 Roo.data.DataReader = function(meta, recordType){
11986     
11987     this.meta = meta;
11988     
11989     this.recordType = recordType instanceof Array ? 
11990         Roo.data.Record.create(recordType) : recordType;
11991 };
11992
11993 Roo.data.DataReader.prototype = {
11994      /**
11995      * Create an empty record
11996      * @param {Object} data (optional) - overlay some values
11997      * @return {Roo.data.Record} record created.
11998      */
11999     newRow :  function(d) {
12000         var da =  {};
12001         this.recordType.prototype.fields.each(function(c) {
12002             switch( c.type) {
12003                 case 'int' : da[c.name] = 0; break;
12004                 case 'date' : da[c.name] = new Date(); break;
12005                 case 'float' : da[c.name] = 0.0; break;
12006                 case 'boolean' : da[c.name] = false; break;
12007                 default : da[c.name] = ""; break;
12008             }
12009             
12010         });
12011         return new this.recordType(Roo.apply(da, d));
12012     }
12013     
12014 };/*
12015  * Based on:
12016  * Ext JS Library 1.1.1
12017  * Copyright(c) 2006-2007, Ext JS, LLC.
12018  *
12019  * Originally Released Under LGPL - original licence link has changed is not relivant.
12020  *
12021  * Fork - LGPL
12022  * <script type="text/javascript">
12023  */
12024
12025 /**
12026  * @class Roo.data.DataProxy
12027  * @extends Roo.data.Observable
12028  * This class is an abstract base class for implementations which provide retrieval of
12029  * unformatted data objects.<br>
12030  * <p>
12031  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12032  * (of the appropriate type which knows how to parse the data object) to provide a block of
12033  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12034  * <p>
12035  * Custom implementations must implement the load method as described in
12036  * {@link Roo.data.HttpProxy#load}.
12037  */
12038 Roo.data.DataProxy = function(){
12039     this.addEvents({
12040         /**
12041          * @event beforeload
12042          * Fires before a network request is made to retrieve a data object.
12043          * @param {Object} This DataProxy object.
12044          * @param {Object} params The params parameter to the load function.
12045          */
12046         beforeload : true,
12047         /**
12048          * @event load
12049          * Fires before the load method's callback is called.
12050          * @param {Object} This DataProxy object.
12051          * @param {Object} o The data object.
12052          * @param {Object} arg The callback argument object passed to the load function.
12053          */
12054         load : true,
12055         /**
12056          * @event loadexception
12057          * Fires if an Exception occurs during data retrieval.
12058          * @param {Object} This DataProxy object.
12059          * @param {Object} o The data object.
12060          * @param {Object} arg The callback argument object passed to the load function.
12061          * @param {Object} e The Exception.
12062          */
12063         loadexception : true
12064     });
12065     Roo.data.DataProxy.superclass.constructor.call(this);
12066 };
12067
12068 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12069
12070     /**
12071      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12072      */
12073 /*
12074  * Based on:
12075  * Ext JS Library 1.1.1
12076  * Copyright(c) 2006-2007, Ext JS, LLC.
12077  *
12078  * Originally Released Under LGPL - original licence link has changed is not relivant.
12079  *
12080  * Fork - LGPL
12081  * <script type="text/javascript">
12082  */
12083 /**
12084  * @class Roo.data.MemoryProxy
12085  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12086  * to the Reader when its load method is called.
12087  * @constructor
12088  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12089  */
12090 Roo.data.MemoryProxy = function(data){
12091     if (data.data) {
12092         data = data.data;
12093     }
12094     Roo.data.MemoryProxy.superclass.constructor.call(this);
12095     this.data = data;
12096 };
12097
12098 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12099     
12100     /**
12101      * Load data from the requested source (in this case an in-memory
12102      * data object passed to the constructor), read the data object into
12103      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12104      * process that block using the passed callback.
12105      * @param {Object} params This parameter is not used by the MemoryProxy class.
12106      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12107      * object into a block of Roo.data.Records.
12108      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12109      * The function must be passed <ul>
12110      * <li>The Record block object</li>
12111      * <li>The "arg" argument from the load function</li>
12112      * <li>A boolean success indicator</li>
12113      * </ul>
12114      * @param {Object} scope The scope in which to call the callback
12115      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12116      */
12117     load : function(params, reader, callback, scope, arg){
12118         params = params || {};
12119         var result;
12120         try {
12121             result = reader.readRecords(this.data);
12122         }catch(e){
12123             this.fireEvent("loadexception", this, arg, null, e);
12124             callback.call(scope, null, arg, false);
12125             return;
12126         }
12127         callback.call(scope, result, arg, true);
12128     },
12129     
12130     // private
12131     update : function(params, records){
12132         
12133     }
12134 });/*
12135  * Based on:
12136  * Ext JS Library 1.1.1
12137  * Copyright(c) 2006-2007, Ext JS, LLC.
12138  *
12139  * Originally Released Under LGPL - original licence link has changed is not relivant.
12140  *
12141  * Fork - LGPL
12142  * <script type="text/javascript">
12143  */
12144 /**
12145  * @class Roo.data.HttpProxy
12146  * @extends Roo.data.DataProxy
12147  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12148  * configured to reference a certain URL.<br><br>
12149  * <p>
12150  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12151  * from which the running page was served.<br><br>
12152  * <p>
12153  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12154  * <p>
12155  * Be aware that to enable the browser to parse an XML document, the server must set
12156  * the Content-Type header in the HTTP response to "text/xml".
12157  * @constructor
12158  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12159  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12160  * will be used to make the request.
12161  */
12162 Roo.data.HttpProxy = function(conn){
12163     Roo.data.HttpProxy.superclass.constructor.call(this);
12164     // is conn a conn config or a real conn?
12165     this.conn = conn;
12166     this.useAjax = !conn || !conn.events;
12167   
12168 };
12169
12170 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12171     // thse are take from connection...
12172     
12173     /**
12174      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12175      */
12176     /**
12177      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12178      * extra parameters to each request made by this object. (defaults to undefined)
12179      */
12180     /**
12181      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12182      *  to each request made by this object. (defaults to undefined)
12183      */
12184     /**
12185      * @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)
12186      */
12187     /**
12188      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12189      */
12190      /**
12191      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12192      * @type Boolean
12193      */
12194   
12195
12196     /**
12197      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12198      * @type Boolean
12199      */
12200     /**
12201      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12202      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12203      * a finer-grained basis than the DataProxy events.
12204      */
12205     getConnection : function(){
12206         return this.useAjax ? Roo.Ajax : this.conn;
12207     },
12208
12209     /**
12210      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12211      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12212      * process that block using the passed callback.
12213      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12214      * for the request to the remote server.
12215      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12216      * object into a block of Roo.data.Records.
12217      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12218      * The function must be passed <ul>
12219      * <li>The Record block object</li>
12220      * <li>The "arg" argument from the load function</li>
12221      * <li>A boolean success indicator</li>
12222      * </ul>
12223      * @param {Object} scope The scope in which to call the callback
12224      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12225      */
12226     load : function(params, reader, callback, scope, arg){
12227         if(this.fireEvent("beforeload", this, params) !== false){
12228             var  o = {
12229                 params : params || {},
12230                 request: {
12231                     callback : callback,
12232                     scope : scope,
12233                     arg : arg
12234                 },
12235                 reader: reader,
12236                 callback : this.loadResponse,
12237                 scope: this
12238             };
12239             if(this.useAjax){
12240                 Roo.applyIf(o, this.conn);
12241                 if(this.activeRequest){
12242                     Roo.Ajax.abort(this.activeRequest);
12243                 }
12244                 this.activeRequest = Roo.Ajax.request(o);
12245             }else{
12246                 this.conn.request(o);
12247             }
12248         }else{
12249             callback.call(scope||this, null, arg, false);
12250         }
12251     },
12252
12253     // private
12254     loadResponse : function(o, success, response){
12255         delete this.activeRequest;
12256         if(!success){
12257             this.fireEvent("loadexception", this, o, response);
12258             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12259             return;
12260         }
12261         var result;
12262         try {
12263             result = o.reader.read(response);
12264         }catch(e){
12265             this.fireEvent("loadexception", this, o, response, e);
12266             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12267             return;
12268         }
12269         
12270         this.fireEvent("load", this, o, o.request.arg);
12271         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12272     },
12273
12274     // private
12275     update : function(dataSet){
12276
12277     },
12278
12279     // private
12280     updateResponse : function(dataSet){
12281
12282     }
12283 });/*
12284  * Based on:
12285  * Ext JS Library 1.1.1
12286  * Copyright(c) 2006-2007, Ext JS, LLC.
12287  *
12288  * Originally Released Under LGPL - original licence link has changed is not relivant.
12289  *
12290  * Fork - LGPL
12291  * <script type="text/javascript">
12292  */
12293
12294 /**
12295  * @class Roo.data.ScriptTagProxy
12296  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12297  * other than the originating domain of the running page.<br><br>
12298  * <p>
12299  * <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
12300  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12301  * <p>
12302  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12303  * source code that is used as the source inside a &lt;script> tag.<br><br>
12304  * <p>
12305  * In order for the browser to process the returned data, the server must wrap the data object
12306  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12307  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12308  * depending on whether the callback name was passed:
12309  * <p>
12310  * <pre><code>
12311 boolean scriptTag = false;
12312 String cb = request.getParameter("callback");
12313 if (cb != null) {
12314     scriptTag = true;
12315     response.setContentType("text/javascript");
12316 } else {
12317     response.setContentType("application/x-json");
12318 }
12319 Writer out = response.getWriter();
12320 if (scriptTag) {
12321     out.write(cb + "(");
12322 }
12323 out.print(dataBlock.toJsonString());
12324 if (scriptTag) {
12325     out.write(");");
12326 }
12327 </pre></code>
12328  *
12329  * @constructor
12330  * @param {Object} config A configuration object.
12331  */
12332 Roo.data.ScriptTagProxy = function(config){
12333     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12334     Roo.apply(this, config);
12335     this.head = document.getElementsByTagName("head")[0];
12336 };
12337
12338 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12339
12340 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12341     /**
12342      * @cfg {String} url The URL from which to request the data object.
12343      */
12344     /**
12345      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12346      */
12347     timeout : 30000,
12348     /**
12349      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12350      * the server the name of the callback function set up by the load call to process the returned data object.
12351      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12352      * javascript output which calls this named function passing the data object as its only parameter.
12353      */
12354     callbackParam : "callback",
12355     /**
12356      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12357      * name to the request.
12358      */
12359     nocache : true,
12360
12361     /**
12362      * Load data from the configured URL, read the data object into
12363      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12364      * process that block using the passed callback.
12365      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12366      * for the request to the remote server.
12367      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12368      * object into a block of Roo.data.Records.
12369      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12370      * The function must be passed <ul>
12371      * <li>The Record block object</li>
12372      * <li>The "arg" argument from the load function</li>
12373      * <li>A boolean success indicator</li>
12374      * </ul>
12375      * @param {Object} scope The scope in which to call the callback
12376      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12377      */
12378     load : function(params, reader, callback, scope, arg){
12379         if(this.fireEvent("beforeload", this, params) !== false){
12380
12381             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12382
12383             var url = this.url;
12384             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12385             if(this.nocache){
12386                 url += "&_dc=" + (new Date().getTime());
12387             }
12388             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12389             var trans = {
12390                 id : transId,
12391                 cb : "stcCallback"+transId,
12392                 scriptId : "stcScript"+transId,
12393                 params : params,
12394                 arg : arg,
12395                 url : url,
12396                 callback : callback,
12397                 scope : scope,
12398                 reader : reader
12399             };
12400             var conn = this;
12401
12402             window[trans.cb] = function(o){
12403                 conn.handleResponse(o, trans);
12404             };
12405
12406             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12407
12408             if(this.autoAbort !== false){
12409                 this.abort();
12410             }
12411
12412             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12413
12414             var script = document.createElement("script");
12415             script.setAttribute("src", url);
12416             script.setAttribute("type", "text/javascript");
12417             script.setAttribute("id", trans.scriptId);
12418             this.head.appendChild(script);
12419
12420             this.trans = trans;
12421         }else{
12422             callback.call(scope||this, null, arg, false);
12423         }
12424     },
12425
12426     // private
12427     isLoading : function(){
12428         return this.trans ? true : false;
12429     },
12430
12431     /**
12432      * Abort the current server request.
12433      */
12434     abort : function(){
12435         if(this.isLoading()){
12436             this.destroyTrans(this.trans);
12437         }
12438     },
12439
12440     // private
12441     destroyTrans : function(trans, isLoaded){
12442         this.head.removeChild(document.getElementById(trans.scriptId));
12443         clearTimeout(trans.timeoutId);
12444         if(isLoaded){
12445             window[trans.cb] = undefined;
12446             try{
12447                 delete window[trans.cb];
12448             }catch(e){}
12449         }else{
12450             // if hasn't been loaded, wait for load to remove it to prevent script error
12451             window[trans.cb] = function(){
12452                 window[trans.cb] = undefined;
12453                 try{
12454                     delete window[trans.cb];
12455                 }catch(e){}
12456             };
12457         }
12458     },
12459
12460     // private
12461     handleResponse : function(o, trans){
12462         this.trans = false;
12463         this.destroyTrans(trans, true);
12464         var result;
12465         try {
12466             result = trans.reader.readRecords(o);
12467         }catch(e){
12468             this.fireEvent("loadexception", this, o, trans.arg, e);
12469             trans.callback.call(trans.scope||window, null, trans.arg, false);
12470             return;
12471         }
12472         this.fireEvent("load", this, o, trans.arg);
12473         trans.callback.call(trans.scope||window, result, trans.arg, true);
12474     },
12475
12476     // private
12477     handleFailure : function(trans){
12478         this.trans = false;
12479         this.destroyTrans(trans, false);
12480         this.fireEvent("loadexception", this, null, trans.arg);
12481         trans.callback.call(trans.scope||window, null, trans.arg, false);
12482     }
12483 });/*
12484  * Based on:
12485  * Ext JS Library 1.1.1
12486  * Copyright(c) 2006-2007, Ext JS, LLC.
12487  *
12488  * Originally Released Under LGPL - original licence link has changed is not relivant.
12489  *
12490  * Fork - LGPL
12491  * <script type="text/javascript">
12492  */
12493
12494 /**
12495  * @class Roo.data.JsonReader
12496  * @extends Roo.data.DataReader
12497  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12498  * based on mappings in a provided Roo.data.Record constructor.
12499  * 
12500  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12501  * in the reply previously. 
12502  * 
12503  * <p>
12504  * Example code:
12505  * <pre><code>
12506 var RecordDef = Roo.data.Record.create([
12507     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12508     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12509 ]);
12510 var myReader = new Roo.data.JsonReader({
12511     totalProperty: "results",    // The property which contains the total dataset size (optional)
12512     root: "rows",                // The property which contains an Array of row objects
12513     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12514 }, RecordDef);
12515 </code></pre>
12516  * <p>
12517  * This would consume a JSON file like this:
12518  * <pre><code>
12519 { 'results': 2, 'rows': [
12520     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12521     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12522 }
12523 </code></pre>
12524  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12525  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12526  * paged from the remote server.
12527  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12528  * @cfg {String} root name of the property which contains the Array of row objects.
12529  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12530  * @cfg {Array} fields Array of field definition objects
12531  * @constructor
12532  * Create a new JsonReader
12533  * @param {Object} meta Metadata configuration options
12534  * @param {Object} recordType Either an Array of field definition objects,
12535  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12536  */
12537 Roo.data.JsonReader = function(meta, recordType){
12538     
12539     meta = meta || {};
12540     // set some defaults:
12541     Roo.applyIf(meta, {
12542         totalProperty: 'total',
12543         successProperty : 'success',
12544         root : 'data',
12545         id : 'id'
12546     });
12547     
12548     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12549 };
12550 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12551     
12552     /**
12553      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12554      * Used by Store query builder to append _requestMeta to params.
12555      * 
12556      */
12557     metaFromRemote : false,
12558     /**
12559      * This method is only used by a DataProxy which has retrieved data from a remote server.
12560      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12561      * @return {Object} data A data block which is used by an Roo.data.Store object as
12562      * a cache of Roo.data.Records.
12563      */
12564     read : function(response){
12565         var json = response.responseText;
12566        
12567         var o = /* eval:var:o */ eval("("+json+")");
12568         if(!o) {
12569             throw {message: "JsonReader.read: Json object not found"};
12570         }
12571         
12572         if(o.metaData){
12573             
12574             delete this.ef;
12575             this.metaFromRemote = true;
12576             this.meta = o.metaData;
12577             this.recordType = Roo.data.Record.create(o.metaData.fields);
12578             this.onMetaChange(this.meta, this.recordType, o);
12579         }
12580         return this.readRecords(o);
12581     },
12582
12583     // private function a store will implement
12584     onMetaChange : function(meta, recordType, o){
12585
12586     },
12587
12588     /**
12589          * @ignore
12590          */
12591     simpleAccess: function(obj, subsc) {
12592         return obj[subsc];
12593     },
12594
12595         /**
12596          * @ignore
12597          */
12598     getJsonAccessor: function(){
12599         var re = /[\[\.]/;
12600         return function(expr) {
12601             try {
12602                 return(re.test(expr))
12603                     ? new Function("obj", "return obj." + expr)
12604                     : function(obj){
12605                         return obj[expr];
12606                     };
12607             } catch(e){}
12608             return Roo.emptyFn;
12609         };
12610     }(),
12611
12612     /**
12613      * Create a data block containing Roo.data.Records from an XML document.
12614      * @param {Object} o An object which contains an Array of row objects in the property specified
12615      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12616      * which contains the total size of the dataset.
12617      * @return {Object} data A data block which is used by an Roo.data.Store object as
12618      * a cache of Roo.data.Records.
12619      */
12620     readRecords : function(o){
12621         /**
12622          * After any data loads, the raw JSON data is available for further custom processing.
12623          * @type Object
12624          */
12625         this.o = o;
12626         var s = this.meta, Record = this.recordType,
12627             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12628
12629 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12630         if (!this.ef) {
12631             if(s.totalProperty) {
12632                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12633                 }
12634                 if(s.successProperty) {
12635                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12636                 }
12637                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12638                 if (s.id) {
12639                         var g = this.getJsonAccessor(s.id);
12640                         this.getId = function(rec) {
12641                                 var r = g(rec);  
12642                                 return (r === undefined || r === "") ? null : r;
12643                         };
12644                 } else {
12645                         this.getId = function(){return null;};
12646                 }
12647             this.ef = [];
12648             for(var jj = 0; jj < fl; jj++){
12649                 f = fi[jj];
12650                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12651                 this.ef[jj] = this.getJsonAccessor(map);
12652             }
12653         }
12654
12655         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12656         if(s.totalProperty){
12657             var vt = parseInt(this.getTotal(o), 10);
12658             if(!isNaN(vt)){
12659                 totalRecords = vt;
12660             }
12661         }
12662         if(s.successProperty){
12663             var vs = this.getSuccess(o);
12664             if(vs === false || vs === 'false'){
12665                 success = false;
12666             }
12667         }
12668         var records = [];
12669         for(var i = 0; i < c; i++){
12670                 var n = root[i];
12671             var values = {};
12672             var id = this.getId(n);
12673             for(var j = 0; j < fl; j++){
12674                 f = fi[j];
12675             var v = this.ef[j](n);
12676             if (!f.convert) {
12677                 Roo.log('missing convert for ' + f.name);
12678                 Roo.log(f);
12679                 continue;
12680             }
12681             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12682             }
12683             var record = new Record(values, id);
12684             record.json = n;
12685             records[i] = record;
12686         }
12687         return {
12688             raw : o,
12689             success : success,
12690             records : records,
12691             totalRecords : totalRecords
12692         };
12693     }
12694 });/*
12695  * Based on:
12696  * Ext JS Library 1.1.1
12697  * Copyright(c) 2006-2007, Ext JS, LLC.
12698  *
12699  * Originally Released Under LGPL - original licence link has changed is not relivant.
12700  *
12701  * Fork - LGPL
12702  * <script type="text/javascript">
12703  */
12704
12705 /**
12706  * @class Roo.data.ArrayReader
12707  * @extends Roo.data.DataReader
12708  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12709  * Each element of that Array represents a row of data fields. The
12710  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12711  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12712  * <p>
12713  * Example code:.
12714  * <pre><code>
12715 var RecordDef = Roo.data.Record.create([
12716     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12717     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12718 ]);
12719 var myReader = new Roo.data.ArrayReader({
12720     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12721 }, RecordDef);
12722 </code></pre>
12723  * <p>
12724  * This would consume an Array like this:
12725  * <pre><code>
12726 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12727   </code></pre>
12728  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12729  * @constructor
12730  * Create a new JsonReader
12731  * @param {Object} meta Metadata configuration options.
12732  * @param {Object} recordType Either an Array of field definition objects
12733  * as specified to {@link Roo.data.Record#create},
12734  * or an {@link Roo.data.Record} object
12735  * created using {@link Roo.data.Record#create}.
12736  */
12737 Roo.data.ArrayReader = function(meta, recordType){
12738     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12739 };
12740
12741 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12742     /**
12743      * Create a data block containing Roo.data.Records from an XML document.
12744      * @param {Object} o An Array of row objects which represents the dataset.
12745      * @return {Object} data A data block which is used by an Roo.data.Store object as
12746      * a cache of Roo.data.Records.
12747      */
12748     readRecords : function(o){
12749         var sid = this.meta ? this.meta.id : null;
12750         var recordType = this.recordType, fields = recordType.prototype.fields;
12751         var records = [];
12752         var root = o;
12753             for(var i = 0; i < root.length; i++){
12754                     var n = root[i];
12755                 var values = {};
12756                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12757                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12758                 var f = fields.items[j];
12759                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12760                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12761                 v = f.convert(v);
12762                 values[f.name] = v;
12763             }
12764                 var record = new recordType(values, id);
12765                 record.json = n;
12766                 records[records.length] = record;
12767             }
12768             return {
12769                 records : records,
12770                 totalRecords : records.length
12771             };
12772     }
12773 });/*
12774  * - LGPL
12775  * * 
12776  */
12777
12778 /**
12779  * @class Roo.bootstrap.ComboBox
12780  * @extends Roo.bootstrap.TriggerField
12781  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12782  * @cfg {Boolean} append (true|false) default false
12783  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12784  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12785  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12786  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12787  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12788  * @cfg {Boolean} animate default true
12789  * @cfg {Boolean} emptyResultText only for touch device
12790  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12791  * @cfg {String} emptyTitle default ''
12792  * @constructor
12793  * Create a new ComboBox.
12794  * @param {Object} config Configuration options
12795  */
12796 Roo.bootstrap.ComboBox = function(config){
12797     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12798     this.addEvents({
12799         /**
12800          * @event expand
12801          * Fires when the dropdown list is expanded
12802         * @param {Roo.bootstrap.ComboBox} combo This combo box
12803         */
12804         'expand' : true,
12805         /**
12806          * @event collapse
12807          * Fires when the dropdown list is collapsed
12808         * @param {Roo.bootstrap.ComboBox} combo This combo box
12809         */
12810         'collapse' : true,
12811         /**
12812          * @event beforeselect
12813          * Fires before a list item is selected. Return false to cancel the selection.
12814         * @param {Roo.bootstrap.ComboBox} combo This combo box
12815         * @param {Roo.data.Record} record The data record returned from the underlying store
12816         * @param {Number} index The index of the selected item in the dropdown list
12817         */
12818         'beforeselect' : true,
12819         /**
12820          * @event select
12821          * Fires when a list item is selected
12822         * @param {Roo.bootstrap.ComboBox} combo This combo box
12823         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12824         * @param {Number} index The index of the selected item in the dropdown list
12825         */
12826         'select' : true,
12827         /**
12828          * @event beforequery
12829          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12830          * The event object passed has these properties:
12831         * @param {Roo.bootstrap.ComboBox} combo This combo box
12832         * @param {String} query The query
12833         * @param {Boolean} forceAll true to force "all" query
12834         * @param {Boolean} cancel true to cancel the query
12835         * @param {Object} e The query event object
12836         */
12837         'beforequery': true,
12838          /**
12839          * @event add
12840          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12841         * @param {Roo.bootstrap.ComboBox} combo This combo box
12842         */
12843         'add' : true,
12844         /**
12845          * @event edit
12846          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12847         * @param {Roo.bootstrap.ComboBox} combo This combo box
12848         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12849         */
12850         'edit' : true,
12851         /**
12852          * @event remove
12853          * Fires when the remove value from the combobox array
12854         * @param {Roo.bootstrap.ComboBox} combo This combo box
12855         */
12856         'remove' : true,
12857         /**
12858          * @event afterremove
12859          * Fires when the remove value from the combobox array
12860         * @param {Roo.bootstrap.ComboBox} combo This combo box
12861         */
12862         'afterremove' : true,
12863         /**
12864          * @event specialfilter
12865          * Fires when specialfilter
12866             * @param {Roo.bootstrap.ComboBox} combo This combo box
12867             */
12868         'specialfilter' : true,
12869         /**
12870          * @event tick
12871          * Fires when tick the element
12872             * @param {Roo.bootstrap.ComboBox} combo This combo box
12873             */
12874         'tick' : true,
12875         /**
12876          * @event touchviewdisplay
12877          * Fires when touch view require special display (default is using displayField)
12878             * @param {Roo.bootstrap.ComboBox} combo This combo box
12879             * @param {Object} cfg set html .
12880             */
12881         'touchviewdisplay' : true
12882         
12883     });
12884     
12885     this.item = [];
12886     this.tickItems = [];
12887     
12888     this.selectedIndex = -1;
12889     if(this.mode == 'local'){
12890         if(config.queryDelay === undefined){
12891             this.queryDelay = 10;
12892         }
12893         if(config.minChars === undefined){
12894             this.minChars = 0;
12895         }
12896     }
12897 };
12898
12899 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12900      
12901     /**
12902      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12903      * rendering into an Roo.Editor, defaults to false)
12904      */
12905     /**
12906      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12907      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12908      */
12909     /**
12910      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12911      */
12912     /**
12913      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12914      * the dropdown list (defaults to undefined, with no header element)
12915      */
12916
12917      /**
12918      * @cfg {String/Roo.Template} tpl The template to use to render the output
12919      */
12920      
12921      /**
12922      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12923      */
12924     listWidth: undefined,
12925     /**
12926      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12927      * mode = 'remote' or 'text' if mode = 'local')
12928      */
12929     displayField: undefined,
12930     
12931     /**
12932      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12933      * mode = 'remote' or 'value' if mode = 'local'). 
12934      * Note: use of a valueField requires the user make a selection
12935      * in order for a value to be mapped.
12936      */
12937     valueField: undefined,
12938     /**
12939      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12940      */
12941     modalTitle : '',
12942     
12943     /**
12944      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12945      * field's data value (defaults to the underlying DOM element's name)
12946      */
12947     hiddenName: undefined,
12948     /**
12949      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12950      */
12951     listClass: '',
12952     /**
12953      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12954      */
12955     selectedClass: 'active',
12956     
12957     /**
12958      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12959      */
12960     shadow:'sides',
12961     /**
12962      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12963      * anchor positions (defaults to 'tl-bl')
12964      */
12965     listAlign: 'tl-bl?',
12966     /**
12967      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12968      */
12969     maxHeight: 300,
12970     /**
12971      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12972      * query specified by the allQuery config option (defaults to 'query')
12973      */
12974     triggerAction: 'query',
12975     /**
12976      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12977      * (defaults to 4, does not apply if editable = false)
12978      */
12979     minChars : 4,
12980     /**
12981      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12982      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12983      */
12984     typeAhead: false,
12985     /**
12986      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12987      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12988      */
12989     queryDelay: 500,
12990     /**
12991      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12992      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12993      */
12994     pageSize: 0,
12995     /**
12996      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12997      * when editable = true (defaults to false)
12998      */
12999     selectOnFocus:false,
13000     /**
13001      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13002      */
13003     queryParam: 'query',
13004     /**
13005      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13006      * when mode = 'remote' (defaults to 'Loading...')
13007      */
13008     loadingText: 'Loading...',
13009     /**
13010      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13011      */
13012     resizable: false,
13013     /**
13014      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13015      */
13016     handleHeight : 8,
13017     /**
13018      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13019      * traditional select (defaults to true)
13020      */
13021     editable: true,
13022     /**
13023      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13024      */
13025     allQuery: '',
13026     /**
13027      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13028      */
13029     mode: 'remote',
13030     /**
13031      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13032      * listWidth has a higher value)
13033      */
13034     minListWidth : 70,
13035     /**
13036      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13037      * allow the user to set arbitrary text into the field (defaults to false)
13038      */
13039     forceSelection:false,
13040     /**
13041      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13042      * if typeAhead = true (defaults to 250)
13043      */
13044     typeAheadDelay : 250,
13045     /**
13046      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13047      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13048      */
13049     valueNotFoundText : undefined,
13050     /**
13051      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13052      */
13053     blockFocus : false,
13054     
13055     /**
13056      * @cfg {Boolean} disableClear Disable showing of clear button.
13057      */
13058     disableClear : false,
13059     /**
13060      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13061      */
13062     alwaysQuery : false,
13063     
13064     /**
13065      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13066      */
13067     multiple : false,
13068     
13069     /**
13070      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13071      */
13072     invalidClass : "has-warning",
13073     
13074     /**
13075      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13076      */
13077     validClass : "has-success",
13078     
13079     /**
13080      * @cfg {Boolean} specialFilter (true|false) special filter default false
13081      */
13082     specialFilter : false,
13083     
13084     /**
13085      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13086      */
13087     mobileTouchView : true,
13088     
13089     /**
13090      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13091      */
13092     useNativeIOS : false,
13093     
13094     /**
13095      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13096      */
13097     mobile_restrict_height : false,
13098     
13099     ios_options : false,
13100     
13101     //private
13102     addicon : false,
13103     editicon: false,
13104     
13105     page: 0,
13106     hasQuery: false,
13107     append: false,
13108     loadNext: false,
13109     autoFocus : true,
13110     tickable : false,
13111     btnPosition : 'right',
13112     triggerList : true,
13113     showToggleBtn : true,
13114     animate : true,
13115     emptyResultText: 'Empty',
13116     triggerText : 'Select',
13117     emptyTitle : '',
13118     
13119     // element that contains real text value.. (when hidden is used..)
13120     
13121     getAutoCreate : function()
13122     {   
13123         var cfg = false;
13124         //render
13125         /*
13126          * Render classic select for iso
13127          */
13128         
13129         if(Roo.isIOS && this.useNativeIOS){
13130             cfg = this.getAutoCreateNativeIOS();
13131             return cfg;
13132         }
13133         
13134         /*
13135          * Touch Devices
13136          */
13137         
13138         if(Roo.isTouch && this.mobileTouchView){
13139             cfg = this.getAutoCreateTouchView();
13140             return cfg;;
13141         }
13142         
13143         /*
13144          *  Normal ComboBox
13145          */
13146         if(!this.tickable){
13147             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13148             return cfg;
13149         }
13150         
13151         /*
13152          *  ComboBox with tickable selections
13153          */
13154              
13155         var align = this.labelAlign || this.parentLabelAlign();
13156         
13157         cfg = {
13158             cls : 'form-group roo-combobox-tickable' //input-group
13159         };
13160         
13161         var btn_text_select = '';
13162         var btn_text_done = '';
13163         var btn_text_cancel = '';
13164         
13165         if (this.btn_text_show) {
13166             btn_text_select = 'Select';
13167             btn_text_done = 'Done';
13168             btn_text_cancel = 'Cancel'; 
13169         }
13170         
13171         var buttons = {
13172             tag : 'div',
13173             cls : 'tickable-buttons',
13174             cn : [
13175                 {
13176                     tag : 'button',
13177                     type : 'button',
13178                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13179                     //html : this.triggerText
13180                     html: btn_text_select
13181                 },
13182                 {
13183                     tag : 'button',
13184                     type : 'button',
13185                     name : 'ok',
13186                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13187                     //html : 'Done'
13188                     html: btn_text_done
13189                 },
13190                 {
13191                     tag : 'button',
13192                     type : 'button',
13193                     name : 'cancel',
13194                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13195                     //html : 'Cancel'
13196                     html: btn_text_cancel
13197                 }
13198             ]
13199         };
13200         
13201         if(this.editable){
13202             buttons.cn.unshift({
13203                 tag: 'input',
13204                 cls: 'roo-select2-search-field-input'
13205             });
13206         }
13207         
13208         var _this = this;
13209         
13210         Roo.each(buttons.cn, function(c){
13211             if (_this.size) {
13212                 c.cls += ' btn-' + _this.size;
13213             }
13214
13215             if (_this.disabled) {
13216                 c.disabled = true;
13217             }
13218         });
13219         
13220         var box = {
13221             tag: 'div',
13222             cn: [
13223                 {
13224                     tag: 'input',
13225                     type : 'hidden',
13226                     cls: 'form-hidden-field'
13227                 },
13228                 {
13229                     tag: 'ul',
13230                     cls: 'roo-select2-choices',
13231                     cn:[
13232                         {
13233                             tag: 'li',
13234                             cls: 'roo-select2-search-field',
13235                             cn: [
13236                                 buttons
13237                             ]
13238                         }
13239                     ]
13240                 }
13241             ]
13242         };
13243         
13244         var combobox = {
13245             cls: 'roo-select2-container input-group roo-select2-container-multi',
13246             cn: [
13247                 box
13248 //                {
13249 //                    tag: 'ul',
13250 //                    cls: 'typeahead typeahead-long dropdown-menu',
13251 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13252 //                }
13253             ]
13254         };
13255         
13256         if(this.hasFeedback && !this.allowBlank){
13257             
13258             var feedback = {
13259                 tag: 'span',
13260                 cls: 'glyphicon form-control-feedback'
13261             };
13262
13263             combobox.cn.push(feedback);
13264         }
13265         
13266         
13267         if (align ==='left' && this.fieldLabel.length) {
13268             
13269             cfg.cls += ' roo-form-group-label-left';
13270             
13271             cfg.cn = [
13272                 {
13273                     tag : 'i',
13274                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13275                     tooltip : 'This field is required'
13276                 },
13277                 {
13278                     tag: 'label',
13279                     'for' :  id,
13280                     cls : 'control-label',
13281                     html : this.fieldLabel
13282
13283                 },
13284                 {
13285                     cls : "", 
13286                     cn: [
13287                         combobox
13288                     ]
13289                 }
13290
13291             ];
13292             
13293             var labelCfg = cfg.cn[1];
13294             var contentCfg = cfg.cn[2];
13295             
13296
13297             if(this.indicatorpos == 'right'){
13298                 
13299                 cfg.cn = [
13300                     {
13301                         tag: 'label',
13302                         'for' :  id,
13303                         cls : 'control-label',
13304                         cn : [
13305                             {
13306                                 tag : 'span',
13307                                 html : this.fieldLabel
13308                             },
13309                             {
13310                                 tag : 'i',
13311                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13312                                 tooltip : 'This field is required'
13313                             }
13314                         ]
13315                     },
13316                     {
13317                         cls : "",
13318                         cn: [
13319                             combobox
13320                         ]
13321                     }
13322
13323                 ];
13324                 
13325                 
13326                 
13327                 labelCfg = cfg.cn[0];
13328                 contentCfg = cfg.cn[1];
13329             
13330             }
13331             
13332             if(this.labelWidth > 12){
13333                 labelCfg.style = "width: " + this.labelWidth + 'px';
13334             }
13335             
13336             if(this.labelWidth < 13 && this.labelmd == 0){
13337                 this.labelmd = this.labelWidth;
13338             }
13339             
13340             if(this.labellg > 0){
13341                 labelCfg.cls += ' col-lg-' + this.labellg;
13342                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13343             }
13344             
13345             if(this.labelmd > 0){
13346                 labelCfg.cls += ' col-md-' + this.labelmd;
13347                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13348             }
13349             
13350             if(this.labelsm > 0){
13351                 labelCfg.cls += ' col-sm-' + this.labelsm;
13352                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13353             }
13354             
13355             if(this.labelxs > 0){
13356                 labelCfg.cls += ' col-xs-' + this.labelxs;
13357                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13358             }
13359                 
13360                 
13361         } else if ( this.fieldLabel.length) {
13362 //                Roo.log(" label");
13363                  cfg.cn = [
13364                     {
13365                         tag : 'i',
13366                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13367                         tooltip : 'This field is required'
13368                     },
13369                     {
13370                         tag: 'label',
13371                         //cls : 'input-group-addon',
13372                         html : this.fieldLabel
13373                     },
13374                     combobox
13375                 ];
13376                 
13377                 if(this.indicatorpos == 'right'){
13378                     cfg.cn = [
13379                         {
13380                             tag: 'label',
13381                             //cls : 'input-group-addon',
13382                             html : this.fieldLabel
13383                         },
13384                         {
13385                             tag : 'i',
13386                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13387                             tooltip : 'This field is required'
13388                         },
13389                         combobox
13390                     ];
13391                     
13392                 }
13393
13394         } else {
13395             
13396 //                Roo.log(" no label && no align");
13397                 cfg = combobox
13398                      
13399                 
13400         }
13401          
13402         var settings=this;
13403         ['xs','sm','md','lg'].map(function(size){
13404             if (settings[size]) {
13405                 cfg.cls += ' col-' + size + '-' + settings[size];
13406             }
13407         });
13408         
13409         return cfg;
13410         
13411     },
13412     
13413     _initEventsCalled : false,
13414     
13415     // private
13416     initEvents: function()
13417     {   
13418         if (this._initEventsCalled) { // as we call render... prevent looping...
13419             return;
13420         }
13421         this._initEventsCalled = true;
13422         
13423         if (!this.store) {
13424             throw "can not find store for combo";
13425         }
13426         
13427         this.indicator = this.indicatorEl();
13428         
13429         this.store = Roo.factory(this.store, Roo.data);
13430         this.store.parent = this;
13431         
13432         // if we are building from html. then this element is so complex, that we can not really
13433         // use the rendered HTML.
13434         // so we have to trash and replace the previous code.
13435         if (Roo.XComponent.build_from_html) {
13436             // remove this element....
13437             var e = this.el.dom, k=0;
13438             while (e ) { e = e.previousSibling;  ++k;}
13439
13440             this.el.remove();
13441             
13442             this.el=false;
13443             this.rendered = false;
13444             
13445             this.render(this.parent().getChildContainer(true), k);
13446         }
13447         
13448         if(Roo.isIOS && this.useNativeIOS){
13449             this.initIOSView();
13450             return;
13451         }
13452         
13453         /*
13454          * Touch Devices
13455          */
13456         
13457         if(Roo.isTouch && this.mobileTouchView){
13458             this.initTouchView();
13459             return;
13460         }
13461         
13462         if(this.tickable){
13463             this.initTickableEvents();
13464             return;
13465         }
13466         
13467         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13468         
13469         if(this.hiddenName){
13470             
13471             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13472             
13473             this.hiddenField.dom.value =
13474                 this.hiddenValue !== undefined ? this.hiddenValue :
13475                 this.value !== undefined ? this.value : '';
13476
13477             // prevent input submission
13478             this.el.dom.removeAttribute('name');
13479             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13480              
13481              
13482         }
13483         //if(Roo.isGecko){
13484         //    this.el.dom.setAttribute('autocomplete', 'off');
13485         //}
13486         
13487         var cls = 'x-combo-list';
13488         
13489         //this.list = new Roo.Layer({
13490         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13491         //});
13492         
13493         var _this = this;
13494         
13495         (function(){
13496             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13497             _this.list.setWidth(lw);
13498         }).defer(100);
13499         
13500         this.list.on('mouseover', this.onViewOver, this);
13501         this.list.on('mousemove', this.onViewMove, this);
13502         this.list.on('scroll', this.onViewScroll, this);
13503         
13504         /*
13505         this.list.swallowEvent('mousewheel');
13506         this.assetHeight = 0;
13507
13508         if(this.title){
13509             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13510             this.assetHeight += this.header.getHeight();
13511         }
13512
13513         this.innerList = this.list.createChild({cls:cls+'-inner'});
13514         this.innerList.on('mouseover', this.onViewOver, this);
13515         this.innerList.on('mousemove', this.onViewMove, this);
13516         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13517         
13518         if(this.allowBlank && !this.pageSize && !this.disableClear){
13519             this.footer = this.list.createChild({cls:cls+'-ft'});
13520             this.pageTb = new Roo.Toolbar(this.footer);
13521            
13522         }
13523         if(this.pageSize){
13524             this.footer = this.list.createChild({cls:cls+'-ft'});
13525             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13526                     {pageSize: this.pageSize});
13527             
13528         }
13529         
13530         if (this.pageTb && this.allowBlank && !this.disableClear) {
13531             var _this = this;
13532             this.pageTb.add(new Roo.Toolbar.Fill(), {
13533                 cls: 'x-btn-icon x-btn-clear',
13534                 text: '&#160;',
13535                 handler: function()
13536                 {
13537                     _this.collapse();
13538                     _this.clearValue();
13539                     _this.onSelect(false, -1);
13540                 }
13541             });
13542         }
13543         if (this.footer) {
13544             this.assetHeight += this.footer.getHeight();
13545         }
13546         */
13547             
13548         if(!this.tpl){
13549             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13550         }
13551
13552         this.view = new Roo.View(this.list, this.tpl, {
13553             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13554         });
13555         //this.view.wrapEl.setDisplayed(false);
13556         this.view.on('click', this.onViewClick, this);
13557         
13558         
13559         this.store.on('beforeload', this.onBeforeLoad, this);
13560         this.store.on('load', this.onLoad, this);
13561         this.store.on('loadexception', this.onLoadException, this);
13562         /*
13563         if(this.resizable){
13564             this.resizer = new Roo.Resizable(this.list,  {
13565                pinned:true, handles:'se'
13566             });
13567             this.resizer.on('resize', function(r, w, h){
13568                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13569                 this.listWidth = w;
13570                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13571                 this.restrictHeight();
13572             }, this);
13573             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13574         }
13575         */
13576         if(!this.editable){
13577             this.editable = true;
13578             this.setEditable(false);
13579         }
13580         
13581         /*
13582         
13583         if (typeof(this.events.add.listeners) != 'undefined') {
13584             
13585             this.addicon = this.wrap.createChild(
13586                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13587        
13588             this.addicon.on('click', function(e) {
13589                 this.fireEvent('add', this);
13590             }, this);
13591         }
13592         if (typeof(this.events.edit.listeners) != 'undefined') {
13593             
13594             this.editicon = this.wrap.createChild(
13595                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13596             if (this.addicon) {
13597                 this.editicon.setStyle('margin-left', '40px');
13598             }
13599             this.editicon.on('click', function(e) {
13600                 
13601                 // we fire even  if inothing is selected..
13602                 this.fireEvent('edit', this, this.lastData );
13603                 
13604             }, this);
13605         }
13606         */
13607         
13608         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13609             "up" : function(e){
13610                 this.inKeyMode = true;
13611                 this.selectPrev();
13612             },
13613
13614             "down" : function(e){
13615                 if(!this.isExpanded()){
13616                     this.onTriggerClick();
13617                 }else{
13618                     this.inKeyMode = true;
13619                     this.selectNext();
13620                 }
13621             },
13622
13623             "enter" : function(e){
13624 //                this.onViewClick();
13625                 //return true;
13626                 this.collapse();
13627                 
13628                 if(this.fireEvent("specialkey", this, e)){
13629                     this.onViewClick(false);
13630                 }
13631                 
13632                 return true;
13633             },
13634
13635             "esc" : function(e){
13636                 this.collapse();
13637             },
13638
13639             "tab" : function(e){
13640                 this.collapse();
13641                 
13642                 if(this.fireEvent("specialkey", this, e)){
13643                     this.onViewClick(false);
13644                 }
13645                 
13646                 return true;
13647             },
13648
13649             scope : this,
13650
13651             doRelay : function(foo, bar, hname){
13652                 if(hname == 'down' || this.scope.isExpanded()){
13653                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13654                 }
13655                 return true;
13656             },
13657
13658             forceKeyDown: true
13659         });
13660         
13661         
13662         this.queryDelay = Math.max(this.queryDelay || 10,
13663                 this.mode == 'local' ? 10 : 250);
13664         
13665         
13666         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13667         
13668         if(this.typeAhead){
13669             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13670         }
13671         if(this.editable !== false){
13672             this.inputEl().on("keyup", this.onKeyUp, this);
13673         }
13674         if(this.forceSelection){
13675             this.inputEl().on('blur', this.doForce, this);
13676         }
13677         
13678         if(this.multiple){
13679             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13680             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13681         }
13682     },
13683     
13684     initTickableEvents: function()
13685     {   
13686         this.createList();
13687         
13688         if(this.hiddenName){
13689             
13690             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13691             
13692             this.hiddenField.dom.value =
13693                 this.hiddenValue !== undefined ? this.hiddenValue :
13694                 this.value !== undefined ? this.value : '';
13695
13696             // prevent input submission
13697             this.el.dom.removeAttribute('name');
13698             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13699              
13700              
13701         }
13702         
13703 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13704         
13705         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13706         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13707         if(this.triggerList){
13708             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13709         }
13710          
13711         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13712         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13713         
13714         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13715         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13716         
13717         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13718         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13719         
13720         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13721         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13722         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13723         
13724         this.okBtn.hide();
13725         this.cancelBtn.hide();
13726         
13727         var _this = this;
13728         
13729         (function(){
13730             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13731             _this.list.setWidth(lw);
13732         }).defer(100);
13733         
13734         this.list.on('mouseover', this.onViewOver, this);
13735         this.list.on('mousemove', this.onViewMove, this);
13736         
13737         this.list.on('scroll', this.onViewScroll, this);
13738         
13739         if(!this.tpl){
13740             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13741                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13742         }
13743
13744         this.view = new Roo.View(this.list, this.tpl, {
13745             singleSelect:true,
13746             tickable:true,
13747             parent:this,
13748             store: this.store,
13749             selectedClass: this.selectedClass
13750         });
13751         
13752         //this.view.wrapEl.setDisplayed(false);
13753         this.view.on('click', this.onViewClick, this);
13754         
13755         
13756         
13757         this.store.on('beforeload', this.onBeforeLoad, this);
13758         this.store.on('load', this.onLoad, this);
13759         this.store.on('loadexception', this.onLoadException, this);
13760         
13761         if(this.editable){
13762             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13763                 "up" : function(e){
13764                     this.inKeyMode = true;
13765                     this.selectPrev();
13766                 },
13767
13768                 "down" : function(e){
13769                     this.inKeyMode = true;
13770                     this.selectNext();
13771                 },
13772
13773                 "enter" : function(e){
13774                     if(this.fireEvent("specialkey", this, e)){
13775                         this.onViewClick(false);
13776                     }
13777                     
13778                     return true;
13779                 },
13780
13781                 "esc" : function(e){
13782                     this.onTickableFooterButtonClick(e, false, false);
13783                 },
13784
13785                 "tab" : function(e){
13786                     this.fireEvent("specialkey", this, e);
13787                     
13788                     this.onTickableFooterButtonClick(e, false, false);
13789                     
13790                     return true;
13791                 },
13792
13793                 scope : this,
13794
13795                 doRelay : function(e, fn, key){
13796                     if(this.scope.isExpanded()){
13797                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13798                     }
13799                     return true;
13800                 },
13801
13802                 forceKeyDown: true
13803             });
13804         }
13805         
13806         this.queryDelay = Math.max(this.queryDelay || 10,
13807                 this.mode == 'local' ? 10 : 250);
13808         
13809         
13810         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13811         
13812         if(this.typeAhead){
13813             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13814         }
13815         
13816         if(this.editable !== false){
13817             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13818         }
13819         
13820         this.indicator = this.indicatorEl();
13821         
13822         if(this.indicator){
13823             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13824             this.indicator.hide();
13825         }
13826         
13827     },
13828
13829     onDestroy : function(){
13830         if(this.view){
13831             this.view.setStore(null);
13832             this.view.el.removeAllListeners();
13833             this.view.el.remove();
13834             this.view.purgeListeners();
13835         }
13836         if(this.list){
13837             this.list.dom.innerHTML  = '';
13838         }
13839         
13840         if(this.store){
13841             this.store.un('beforeload', this.onBeforeLoad, this);
13842             this.store.un('load', this.onLoad, this);
13843             this.store.un('loadexception', this.onLoadException, this);
13844         }
13845         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13846     },
13847
13848     // private
13849     fireKey : function(e){
13850         if(e.isNavKeyPress() && !this.list.isVisible()){
13851             this.fireEvent("specialkey", this, e);
13852         }
13853     },
13854
13855     // private
13856     onResize: function(w, h){
13857 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13858 //        
13859 //        if(typeof w != 'number'){
13860 //            // we do not handle it!?!?
13861 //            return;
13862 //        }
13863 //        var tw = this.trigger.getWidth();
13864 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13865 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13866 //        var x = w - tw;
13867 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13868 //            
13869 //        //this.trigger.setStyle('left', x+'px');
13870 //        
13871 //        if(this.list && this.listWidth === undefined){
13872 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13873 //            this.list.setWidth(lw);
13874 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13875 //        }
13876         
13877     
13878         
13879     },
13880
13881     /**
13882      * Allow or prevent the user from directly editing the field text.  If false is passed,
13883      * the user will only be able to select from the items defined in the dropdown list.  This method
13884      * is the runtime equivalent of setting the 'editable' config option at config time.
13885      * @param {Boolean} value True to allow the user to directly edit the field text
13886      */
13887     setEditable : function(value){
13888         if(value == this.editable){
13889             return;
13890         }
13891         this.editable = value;
13892         if(!value){
13893             this.inputEl().dom.setAttribute('readOnly', true);
13894             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13895             this.inputEl().addClass('x-combo-noedit');
13896         }else{
13897             this.inputEl().dom.setAttribute('readOnly', false);
13898             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13899             this.inputEl().removeClass('x-combo-noedit');
13900         }
13901     },
13902
13903     // private
13904     
13905     onBeforeLoad : function(combo,opts){
13906         if(!this.hasFocus){
13907             return;
13908         }
13909          if (!opts.add) {
13910             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13911          }
13912         this.restrictHeight();
13913         this.selectedIndex = -1;
13914     },
13915
13916     // private
13917     onLoad : function(){
13918         
13919         this.hasQuery = false;
13920         
13921         if(!this.hasFocus){
13922             return;
13923         }
13924         
13925         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13926             this.loading.hide();
13927         }
13928         
13929         if(this.store.getCount() > 0){
13930             
13931             this.expand();
13932             this.restrictHeight();
13933             if(this.lastQuery == this.allQuery){
13934                 if(this.editable && !this.tickable){
13935                     this.inputEl().dom.select();
13936                 }
13937                 
13938                 if(
13939                     !this.selectByValue(this.value, true) &&
13940                     this.autoFocus && 
13941                     (
13942                         !this.store.lastOptions ||
13943                         typeof(this.store.lastOptions.add) == 'undefined' || 
13944                         this.store.lastOptions.add != true
13945                     )
13946                 ){
13947                     this.select(0, true);
13948                 }
13949             }else{
13950                 if(this.autoFocus){
13951                     this.selectNext();
13952                 }
13953                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13954                     this.taTask.delay(this.typeAheadDelay);
13955                 }
13956             }
13957         }else{
13958             this.onEmptyResults();
13959         }
13960         
13961         //this.el.focus();
13962     },
13963     // private
13964     onLoadException : function()
13965     {
13966         this.hasQuery = false;
13967         
13968         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13969             this.loading.hide();
13970         }
13971         
13972         if(this.tickable && this.editable){
13973             return;
13974         }
13975         
13976         this.collapse();
13977         // only causes errors at present
13978         //Roo.log(this.store.reader.jsonData);
13979         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13980             // fixme
13981             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13982         //}
13983         
13984         
13985     },
13986     // private
13987     onTypeAhead : function(){
13988         if(this.store.getCount() > 0){
13989             var r = this.store.getAt(0);
13990             var newValue = r.data[this.displayField];
13991             var len = newValue.length;
13992             var selStart = this.getRawValue().length;
13993             
13994             if(selStart != len){
13995                 this.setRawValue(newValue);
13996                 this.selectText(selStart, newValue.length);
13997             }
13998         }
13999     },
14000
14001     // private
14002     onSelect : function(record, index){
14003         
14004         if(this.fireEvent('beforeselect', this, record, index) !== false){
14005         
14006             this.setFromData(index > -1 ? record.data : false);
14007             
14008             this.collapse();
14009             this.fireEvent('select', this, record, index);
14010         }
14011     },
14012
14013     /**
14014      * Returns the currently selected field value or empty string if no value is set.
14015      * @return {String} value The selected value
14016      */
14017     getValue : function()
14018     {
14019         if(Roo.isIOS && this.useNativeIOS){
14020             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14021         }
14022         
14023         if(this.multiple){
14024             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14025         }
14026         
14027         if(this.valueField){
14028             return typeof this.value != 'undefined' ? this.value : '';
14029         }else{
14030             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14031         }
14032     },
14033     
14034     getRawValue : function()
14035     {
14036         if(Roo.isIOS && this.useNativeIOS){
14037             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14038         }
14039         
14040         var v = this.inputEl().getValue();
14041         
14042         return v;
14043     },
14044
14045     /**
14046      * Clears any text/value currently set in the field
14047      */
14048     clearValue : function(){
14049         
14050         if(this.hiddenField){
14051             this.hiddenField.dom.value = '';
14052         }
14053         this.value = '';
14054         this.setRawValue('');
14055         this.lastSelectionText = '';
14056         this.lastData = false;
14057         
14058         var close = this.closeTriggerEl();
14059         
14060         if(close){
14061             close.hide();
14062         }
14063         
14064         this.validate();
14065         
14066     },
14067
14068     /**
14069      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14070      * will be displayed in the field.  If the value does not match the data value of an existing item,
14071      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14072      * Otherwise the field will be blank (although the value will still be set).
14073      * @param {String} value The value to match
14074      */
14075     setValue : function(v)
14076     {
14077         if(Roo.isIOS && this.useNativeIOS){
14078             this.setIOSValue(v);
14079             return;
14080         }
14081         
14082         if(this.multiple){
14083             this.syncValue();
14084             return;
14085         }
14086         
14087         var text = v;
14088         if(this.valueField){
14089             var r = this.findRecord(this.valueField, v);
14090             if(r){
14091                 text = r.data[this.displayField];
14092             }else if(this.valueNotFoundText !== undefined){
14093                 text = this.valueNotFoundText;
14094             }
14095         }
14096         this.lastSelectionText = text;
14097         if(this.hiddenField){
14098             this.hiddenField.dom.value = v;
14099         }
14100         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14101         this.value = v;
14102         
14103         var close = this.closeTriggerEl();
14104         
14105         if(close){
14106             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14107         }
14108         
14109         this.validate();
14110     },
14111     /**
14112      * @property {Object} the last set data for the element
14113      */
14114     
14115     lastData : false,
14116     /**
14117      * Sets the value of the field based on a object which is related to the record format for the store.
14118      * @param {Object} value the value to set as. or false on reset?
14119      */
14120     setFromData : function(o){
14121         
14122         if(this.multiple){
14123             this.addItem(o);
14124             return;
14125         }
14126             
14127         var dv = ''; // display value
14128         var vv = ''; // value value..
14129         this.lastData = o;
14130         if (this.displayField) {
14131             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14132         } else {
14133             // this is an error condition!!!
14134             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14135         }
14136         
14137         if(this.valueField){
14138             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14139         }
14140         
14141         var close = this.closeTriggerEl();
14142         
14143         if(close){
14144             if(dv.length || vv * 1 > 0){
14145                 close.show() ;
14146                 this.blockFocus=true;
14147             } else {
14148                 close.hide();
14149             }             
14150         }
14151         
14152         if(this.hiddenField){
14153             this.hiddenField.dom.value = vv;
14154             
14155             this.lastSelectionText = dv;
14156             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14157             this.value = vv;
14158             return;
14159         }
14160         // no hidden field.. - we store the value in 'value', but still display
14161         // display field!!!!
14162         this.lastSelectionText = dv;
14163         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14164         this.value = vv;
14165         
14166         
14167         
14168     },
14169     // private
14170     reset : function(){
14171         // overridden so that last data is reset..
14172         
14173         if(this.multiple){
14174             this.clearItem();
14175             return;
14176         }
14177         
14178         this.setValue(this.originalValue);
14179         //this.clearInvalid();
14180         this.lastData = false;
14181         if (this.view) {
14182             this.view.clearSelections();
14183         }
14184         
14185         this.validate();
14186     },
14187     // private
14188     findRecord : function(prop, value){
14189         var record;
14190         if(this.store.getCount() > 0){
14191             this.store.each(function(r){
14192                 if(r.data[prop] == value){
14193                     record = r;
14194                     return false;
14195                 }
14196                 return true;
14197             });
14198         }
14199         return record;
14200     },
14201     
14202     getName: function()
14203     {
14204         // returns hidden if it's set..
14205         if (!this.rendered) {return ''};
14206         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14207         
14208     },
14209     // private
14210     onViewMove : function(e, t){
14211         this.inKeyMode = false;
14212     },
14213
14214     // private
14215     onViewOver : function(e, t){
14216         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14217             return;
14218         }
14219         var item = this.view.findItemFromChild(t);
14220         
14221         if(item){
14222             var index = this.view.indexOf(item);
14223             this.select(index, false);
14224         }
14225     },
14226
14227     // private
14228     onViewClick : function(view, doFocus, el, e)
14229     {
14230         var index = this.view.getSelectedIndexes()[0];
14231         
14232         var r = this.store.getAt(index);
14233         
14234         if(this.tickable){
14235             
14236             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14237                 return;
14238             }
14239             
14240             var rm = false;
14241             var _this = this;
14242             
14243             Roo.each(this.tickItems, function(v,k){
14244                 
14245                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14246                     Roo.log(v);
14247                     _this.tickItems.splice(k, 1);
14248                     
14249                     if(typeof(e) == 'undefined' && view == false){
14250                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14251                     }
14252                     
14253                     rm = true;
14254                     return;
14255                 }
14256             });
14257             
14258             if(rm){
14259                 return;
14260             }
14261             
14262             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14263                 this.tickItems.push(r.data);
14264             }
14265             
14266             if(typeof(e) == 'undefined' && view == false){
14267                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14268             }
14269                     
14270             return;
14271         }
14272         
14273         if(r){
14274             this.onSelect(r, index);
14275         }
14276         if(doFocus !== false && !this.blockFocus){
14277             this.inputEl().focus();
14278         }
14279     },
14280
14281     // private
14282     restrictHeight : function(){
14283         //this.innerList.dom.style.height = '';
14284         //var inner = this.innerList.dom;
14285         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14286         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14287         //this.list.beginUpdate();
14288         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14289         this.list.alignTo(this.inputEl(), this.listAlign);
14290         this.list.alignTo(this.inputEl(), this.listAlign);
14291         //this.list.endUpdate();
14292     },
14293
14294     // private
14295     onEmptyResults : function(){
14296         
14297         if(this.tickable && this.editable){
14298             this.hasFocus = false;
14299             this.restrictHeight();
14300             return;
14301         }
14302         
14303         this.collapse();
14304     },
14305
14306     /**
14307      * Returns true if the dropdown list is expanded, else false.
14308      */
14309     isExpanded : function(){
14310         return this.list.isVisible();
14311     },
14312
14313     /**
14314      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14315      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14316      * @param {String} value The data value of the item to select
14317      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14318      * selected item if it is not currently in view (defaults to true)
14319      * @return {Boolean} True if the value matched an item in the list, else false
14320      */
14321     selectByValue : function(v, scrollIntoView){
14322         if(v !== undefined && v !== null){
14323             var r = this.findRecord(this.valueField || this.displayField, v);
14324             if(r){
14325                 this.select(this.store.indexOf(r), scrollIntoView);
14326                 return true;
14327             }
14328         }
14329         return false;
14330     },
14331
14332     /**
14333      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14334      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14335      * @param {Number} index The zero-based index of the list item to select
14336      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14337      * selected item if it is not currently in view (defaults to true)
14338      */
14339     select : function(index, scrollIntoView){
14340         this.selectedIndex = index;
14341         this.view.select(index);
14342         if(scrollIntoView !== false){
14343             var el = this.view.getNode(index);
14344             /*
14345              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14346              */
14347             if(el){
14348                 this.list.scrollChildIntoView(el, false);
14349             }
14350         }
14351     },
14352
14353     // private
14354     selectNext : function(){
14355         var ct = this.store.getCount();
14356         if(ct > 0){
14357             if(this.selectedIndex == -1){
14358                 this.select(0);
14359             }else if(this.selectedIndex < ct-1){
14360                 this.select(this.selectedIndex+1);
14361             }
14362         }
14363     },
14364
14365     // private
14366     selectPrev : function(){
14367         var ct = this.store.getCount();
14368         if(ct > 0){
14369             if(this.selectedIndex == -1){
14370                 this.select(0);
14371             }else if(this.selectedIndex != 0){
14372                 this.select(this.selectedIndex-1);
14373             }
14374         }
14375     },
14376
14377     // private
14378     onKeyUp : function(e){
14379         if(this.editable !== false && !e.isSpecialKey()){
14380             this.lastKey = e.getKey();
14381             this.dqTask.delay(this.queryDelay);
14382         }
14383     },
14384
14385     // private
14386     validateBlur : function(){
14387         return !this.list || !this.list.isVisible();   
14388     },
14389
14390     // private
14391     initQuery : function(){
14392         
14393         var v = this.getRawValue();
14394         
14395         if(this.tickable && this.editable){
14396             v = this.tickableInputEl().getValue();
14397         }
14398         
14399         this.doQuery(v);
14400     },
14401
14402     // private
14403     doForce : function(){
14404         if(this.inputEl().dom.value.length > 0){
14405             this.inputEl().dom.value =
14406                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14407              
14408         }
14409     },
14410
14411     /**
14412      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14413      * query allowing the query action to be canceled if needed.
14414      * @param {String} query The SQL query to execute
14415      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14416      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14417      * saved in the current store (defaults to false)
14418      */
14419     doQuery : function(q, forceAll){
14420         
14421         if(q === undefined || q === null){
14422             q = '';
14423         }
14424         var qe = {
14425             query: q,
14426             forceAll: forceAll,
14427             combo: this,
14428             cancel:false
14429         };
14430         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14431             return false;
14432         }
14433         q = qe.query;
14434         
14435         forceAll = qe.forceAll;
14436         if(forceAll === true || (q.length >= this.minChars)){
14437             
14438             this.hasQuery = true;
14439             
14440             if(this.lastQuery != q || this.alwaysQuery){
14441                 this.lastQuery = q;
14442                 if(this.mode == 'local'){
14443                     this.selectedIndex = -1;
14444                     if(forceAll){
14445                         this.store.clearFilter();
14446                     }else{
14447                         
14448                         if(this.specialFilter){
14449                             this.fireEvent('specialfilter', this);
14450                             this.onLoad();
14451                             return;
14452                         }
14453                         
14454                         this.store.filter(this.displayField, q);
14455                     }
14456                     
14457                     this.store.fireEvent("datachanged", this.store);
14458                     
14459                     this.onLoad();
14460                     
14461                     
14462                 }else{
14463                     
14464                     this.store.baseParams[this.queryParam] = q;
14465                     
14466                     var options = {params : this.getParams(q)};
14467                     
14468                     if(this.loadNext){
14469                         options.add = true;
14470                         options.params.start = this.page * this.pageSize;
14471                     }
14472                     
14473                     this.store.load(options);
14474                     
14475                     /*
14476                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14477                      *  we should expand the list on onLoad
14478                      *  so command out it
14479                      */
14480 //                    this.expand();
14481                 }
14482             }else{
14483                 this.selectedIndex = -1;
14484                 this.onLoad();   
14485             }
14486         }
14487         
14488         this.loadNext = false;
14489     },
14490     
14491     // private
14492     getParams : function(q){
14493         var p = {};
14494         //p[this.queryParam] = q;
14495         
14496         if(this.pageSize){
14497             p.start = 0;
14498             p.limit = this.pageSize;
14499         }
14500         return p;
14501     },
14502
14503     /**
14504      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14505      */
14506     collapse : function(){
14507         if(!this.isExpanded()){
14508             return;
14509         }
14510         
14511         this.list.hide();
14512         
14513         this.hasFocus = false;
14514         
14515         if(this.tickable){
14516             this.okBtn.hide();
14517             this.cancelBtn.hide();
14518             this.trigger.show();
14519             
14520             if(this.editable){
14521                 this.tickableInputEl().dom.value = '';
14522                 this.tickableInputEl().blur();
14523             }
14524             
14525         }
14526         
14527         Roo.get(document).un('mousedown', this.collapseIf, this);
14528         Roo.get(document).un('mousewheel', this.collapseIf, this);
14529         if (!this.editable) {
14530             Roo.get(document).un('keydown', this.listKeyPress, this);
14531         }
14532         this.fireEvent('collapse', this);
14533         
14534         this.validate();
14535     },
14536
14537     // private
14538     collapseIf : function(e){
14539         var in_combo  = e.within(this.el);
14540         var in_list =  e.within(this.list);
14541         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14542         
14543         if (in_combo || in_list || is_list) {
14544             //e.stopPropagation();
14545             return;
14546         }
14547         
14548         if(this.tickable){
14549             this.onTickableFooterButtonClick(e, false, false);
14550         }
14551
14552         this.collapse();
14553         
14554     },
14555
14556     /**
14557      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14558      */
14559     expand : function(){
14560        
14561         if(this.isExpanded() || !this.hasFocus){
14562             return;
14563         }
14564         
14565         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14566         this.list.setWidth(lw);
14567         
14568         Roo.log('expand');
14569         
14570         this.list.show();
14571         
14572         this.restrictHeight();
14573         
14574         if(this.tickable){
14575             
14576             this.tickItems = Roo.apply([], this.item);
14577             
14578             this.okBtn.show();
14579             this.cancelBtn.show();
14580             this.trigger.hide();
14581             
14582             if(this.editable){
14583                 this.tickableInputEl().focus();
14584             }
14585             
14586         }
14587         
14588         Roo.get(document).on('mousedown', this.collapseIf, this);
14589         Roo.get(document).on('mousewheel', this.collapseIf, this);
14590         if (!this.editable) {
14591             Roo.get(document).on('keydown', this.listKeyPress, this);
14592         }
14593         
14594         this.fireEvent('expand', this);
14595     },
14596
14597     // private
14598     // Implements the default empty TriggerField.onTriggerClick function
14599     onTriggerClick : function(e)
14600     {
14601         Roo.log('trigger click');
14602         
14603         if(this.disabled || !this.triggerList){
14604             return;
14605         }
14606         
14607         this.page = 0;
14608         this.loadNext = false;
14609         
14610         if(this.isExpanded()){
14611             this.collapse();
14612             if (!this.blockFocus) {
14613                 this.inputEl().focus();
14614             }
14615             
14616         }else {
14617             this.hasFocus = true;
14618             if(this.triggerAction == 'all') {
14619                 this.doQuery(this.allQuery, true);
14620             } else {
14621                 this.doQuery(this.getRawValue());
14622             }
14623             if (!this.blockFocus) {
14624                 this.inputEl().focus();
14625             }
14626         }
14627     },
14628     
14629     onTickableTriggerClick : function(e)
14630     {
14631         if(this.disabled){
14632             return;
14633         }
14634         
14635         this.page = 0;
14636         this.loadNext = false;
14637         this.hasFocus = true;
14638         
14639         if(this.triggerAction == 'all') {
14640             this.doQuery(this.allQuery, true);
14641         } else {
14642             this.doQuery(this.getRawValue());
14643         }
14644     },
14645     
14646     onSearchFieldClick : function(e)
14647     {
14648         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14649             this.onTickableFooterButtonClick(e, false, false);
14650             return;
14651         }
14652         
14653         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14654             return;
14655         }
14656         
14657         this.page = 0;
14658         this.loadNext = false;
14659         this.hasFocus = true;
14660         
14661         if(this.triggerAction == 'all') {
14662             this.doQuery(this.allQuery, true);
14663         } else {
14664             this.doQuery(this.getRawValue());
14665         }
14666     },
14667     
14668     listKeyPress : function(e)
14669     {
14670         //Roo.log('listkeypress');
14671         // scroll to first matching element based on key pres..
14672         if (e.isSpecialKey()) {
14673             return false;
14674         }
14675         var k = String.fromCharCode(e.getKey()).toUpperCase();
14676         //Roo.log(k);
14677         var match  = false;
14678         var csel = this.view.getSelectedNodes();
14679         var cselitem = false;
14680         if (csel.length) {
14681             var ix = this.view.indexOf(csel[0]);
14682             cselitem  = this.store.getAt(ix);
14683             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14684                 cselitem = false;
14685             }
14686             
14687         }
14688         
14689         this.store.each(function(v) { 
14690             if (cselitem) {
14691                 // start at existing selection.
14692                 if (cselitem.id == v.id) {
14693                     cselitem = false;
14694                 }
14695                 return true;
14696             }
14697                 
14698             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14699                 match = this.store.indexOf(v);
14700                 return false;
14701             }
14702             return true;
14703         }, this);
14704         
14705         if (match === false) {
14706             return true; // no more action?
14707         }
14708         // scroll to?
14709         this.view.select(match);
14710         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14711         sn.scrollIntoView(sn.dom.parentNode, false);
14712     },
14713     
14714     onViewScroll : function(e, t){
14715         
14716         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){
14717             return;
14718         }
14719         
14720         this.hasQuery = true;
14721         
14722         this.loading = this.list.select('.loading', true).first();
14723         
14724         if(this.loading === null){
14725             this.list.createChild({
14726                 tag: 'div',
14727                 cls: 'loading roo-select2-more-results roo-select2-active',
14728                 html: 'Loading more results...'
14729             });
14730             
14731             this.loading = this.list.select('.loading', true).first();
14732             
14733             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14734             
14735             this.loading.hide();
14736         }
14737         
14738         this.loading.show();
14739         
14740         var _combo = this;
14741         
14742         this.page++;
14743         this.loadNext = true;
14744         
14745         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14746         
14747         return;
14748     },
14749     
14750     addItem : function(o)
14751     {   
14752         var dv = ''; // display value
14753         
14754         if (this.displayField) {
14755             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14756         } else {
14757             // this is an error condition!!!
14758             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14759         }
14760         
14761         if(!dv.length){
14762             return;
14763         }
14764         
14765         var choice = this.choices.createChild({
14766             tag: 'li',
14767             cls: 'roo-select2-search-choice',
14768             cn: [
14769                 {
14770                     tag: 'div',
14771                     html: dv
14772                 },
14773                 {
14774                     tag: 'a',
14775                     href: '#',
14776                     cls: 'roo-select2-search-choice-close fa fa-times',
14777                     tabindex: '-1'
14778                 }
14779             ]
14780             
14781         }, this.searchField);
14782         
14783         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14784         
14785         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14786         
14787         this.item.push(o);
14788         
14789         this.lastData = o;
14790         
14791         this.syncValue();
14792         
14793         this.inputEl().dom.value = '';
14794         
14795         this.validate();
14796     },
14797     
14798     onRemoveItem : function(e, _self, o)
14799     {
14800         e.preventDefault();
14801         
14802         this.lastItem = Roo.apply([], this.item);
14803         
14804         var index = this.item.indexOf(o.data) * 1;
14805         
14806         if( index < 0){
14807             Roo.log('not this item?!');
14808             return;
14809         }
14810         
14811         this.item.splice(index, 1);
14812         o.item.remove();
14813         
14814         this.syncValue();
14815         
14816         this.fireEvent('remove', this, e);
14817         
14818         this.validate();
14819         
14820     },
14821     
14822     syncValue : function()
14823     {
14824         if(!this.item.length){
14825             this.clearValue();
14826             return;
14827         }
14828             
14829         var value = [];
14830         var _this = this;
14831         Roo.each(this.item, function(i){
14832             if(_this.valueField){
14833                 value.push(i[_this.valueField]);
14834                 return;
14835             }
14836
14837             value.push(i);
14838         });
14839
14840         this.value = value.join(',');
14841
14842         if(this.hiddenField){
14843             this.hiddenField.dom.value = this.value;
14844         }
14845         
14846         this.store.fireEvent("datachanged", this.store);
14847         
14848         this.validate();
14849     },
14850     
14851     clearItem : function()
14852     {
14853         if(!this.multiple){
14854             return;
14855         }
14856         
14857         this.item = [];
14858         
14859         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14860            c.remove();
14861         });
14862         
14863         this.syncValue();
14864         
14865         this.validate();
14866         
14867         if(this.tickable && !Roo.isTouch){
14868             this.view.refresh();
14869         }
14870     },
14871     
14872     inputEl: function ()
14873     {
14874         if(Roo.isIOS && this.useNativeIOS){
14875             return this.el.select('select.roo-ios-select', true).first();
14876         }
14877         
14878         if(Roo.isTouch && this.mobileTouchView){
14879             return this.el.select('input.form-control',true).first();
14880         }
14881         
14882         if(this.tickable){
14883             return this.searchField;
14884         }
14885         
14886         return this.el.select('input.form-control',true).first();
14887     },
14888     
14889     onTickableFooterButtonClick : function(e, btn, el)
14890     {
14891         e.preventDefault();
14892         
14893         this.lastItem = Roo.apply([], this.item);
14894         
14895         if(btn && btn.name == 'cancel'){
14896             this.tickItems = Roo.apply([], this.item);
14897             this.collapse();
14898             return;
14899         }
14900         
14901         this.clearItem();
14902         
14903         var _this = this;
14904         
14905         Roo.each(this.tickItems, function(o){
14906             _this.addItem(o);
14907         });
14908         
14909         this.collapse();
14910         
14911     },
14912     
14913     validate : function()
14914     {
14915         if(this.getVisibilityEl().hasClass('hidden')){
14916             return true;
14917         }
14918         
14919         var v = this.getRawValue();
14920         
14921         if(this.multiple){
14922             v = this.getValue();
14923         }
14924         
14925         if(this.disabled || this.allowBlank || v.length){
14926             this.markValid();
14927             return true;
14928         }
14929         
14930         this.markInvalid();
14931         return false;
14932     },
14933     
14934     tickableInputEl : function()
14935     {
14936         if(!this.tickable || !this.editable){
14937             return this.inputEl();
14938         }
14939         
14940         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14941     },
14942     
14943     
14944     getAutoCreateTouchView : function()
14945     {
14946         var id = Roo.id();
14947         
14948         var cfg = {
14949             cls: 'form-group' //input-group
14950         };
14951         
14952         var input =  {
14953             tag: 'input',
14954             id : id,
14955             type : this.inputType,
14956             cls : 'form-control x-combo-noedit',
14957             autocomplete: 'new-password',
14958             placeholder : this.placeholder || '',
14959             readonly : true
14960         };
14961         
14962         if (this.name) {
14963             input.name = this.name;
14964         }
14965         
14966         if (this.size) {
14967             input.cls += ' input-' + this.size;
14968         }
14969         
14970         if (this.disabled) {
14971             input.disabled = true;
14972         }
14973         
14974         var inputblock = {
14975             cls : '',
14976             cn : [
14977                 input
14978             ]
14979         };
14980         
14981         if(this.before){
14982             inputblock.cls += ' input-group';
14983             
14984             inputblock.cn.unshift({
14985                 tag :'span',
14986                 cls : 'input-group-addon',
14987                 html : this.before
14988             });
14989         }
14990         
14991         if(this.removable && !this.multiple){
14992             inputblock.cls += ' roo-removable';
14993             
14994             inputblock.cn.push({
14995                 tag: 'button',
14996                 html : 'x',
14997                 cls : 'roo-combo-removable-btn close'
14998             });
14999         }
15000
15001         if(this.hasFeedback && !this.allowBlank){
15002             
15003             inputblock.cls += ' has-feedback';
15004             
15005             inputblock.cn.push({
15006                 tag: 'span',
15007                 cls: 'glyphicon form-control-feedback'
15008             });
15009             
15010         }
15011         
15012         if (this.after) {
15013             
15014             inputblock.cls += (this.before) ? '' : ' input-group';
15015             
15016             inputblock.cn.push({
15017                 tag :'span',
15018                 cls : 'input-group-addon',
15019                 html : this.after
15020             });
15021         }
15022
15023         var box = {
15024             tag: 'div',
15025             cn: [
15026                 {
15027                     tag: 'input',
15028                     type : 'hidden',
15029                     cls: 'form-hidden-field'
15030                 },
15031                 inputblock
15032             ]
15033             
15034         };
15035         
15036         if(this.multiple){
15037             box = {
15038                 tag: 'div',
15039                 cn: [
15040                     {
15041                         tag: 'input',
15042                         type : 'hidden',
15043                         cls: 'form-hidden-field'
15044                     },
15045                     {
15046                         tag: 'ul',
15047                         cls: 'roo-select2-choices',
15048                         cn:[
15049                             {
15050                                 tag: 'li',
15051                                 cls: 'roo-select2-search-field',
15052                                 cn: [
15053
15054                                     inputblock
15055                                 ]
15056                             }
15057                         ]
15058                     }
15059                 ]
15060             }
15061         };
15062         
15063         var combobox = {
15064             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15065             cn: [
15066                 box
15067             ]
15068         };
15069         
15070         if(!this.multiple && this.showToggleBtn){
15071             
15072             var caret = {
15073                         tag: 'span',
15074                         cls: 'caret'
15075             };
15076             
15077             if (this.caret != false) {
15078                 caret = {
15079                      tag: 'i',
15080                      cls: 'fa fa-' + this.caret
15081                 };
15082                 
15083             }
15084             
15085             combobox.cn.push({
15086                 tag :'span',
15087                 cls : 'input-group-addon btn dropdown-toggle',
15088                 cn : [
15089                     caret,
15090                     {
15091                         tag: 'span',
15092                         cls: 'combobox-clear',
15093                         cn  : [
15094                             {
15095                                 tag : 'i',
15096                                 cls: 'icon-remove'
15097                             }
15098                         ]
15099                     }
15100                 ]
15101
15102             })
15103         }
15104         
15105         if(this.multiple){
15106             combobox.cls += ' roo-select2-container-multi';
15107         }
15108         
15109         var align = this.labelAlign || this.parentLabelAlign();
15110         
15111         if (align ==='left' && this.fieldLabel.length) {
15112
15113             cfg.cn = [
15114                 {
15115                    tag : 'i',
15116                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15117                    tooltip : 'This field is required'
15118                 },
15119                 {
15120                     tag: 'label',
15121                     cls : 'control-label',
15122                     html : this.fieldLabel
15123
15124                 },
15125                 {
15126                     cls : '', 
15127                     cn: [
15128                         combobox
15129                     ]
15130                 }
15131             ];
15132             
15133             var labelCfg = cfg.cn[1];
15134             var contentCfg = cfg.cn[2];
15135             
15136
15137             if(this.indicatorpos == 'right'){
15138                 cfg.cn = [
15139                     {
15140                         tag: 'label',
15141                         'for' :  id,
15142                         cls : 'control-label',
15143                         cn : [
15144                             {
15145                                 tag : 'span',
15146                                 html : this.fieldLabel
15147                             },
15148                             {
15149                                 tag : 'i',
15150                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15151                                 tooltip : 'This field is required'
15152                             }
15153                         ]
15154                     },
15155                     {
15156                         cls : "",
15157                         cn: [
15158                             combobox
15159                         ]
15160                     }
15161
15162                 ];
15163                 
15164                 labelCfg = cfg.cn[0];
15165                 contentCfg = cfg.cn[1];
15166             }
15167             
15168            
15169             
15170             if(this.labelWidth > 12){
15171                 labelCfg.style = "width: " + this.labelWidth + 'px';
15172             }
15173             
15174             if(this.labelWidth < 13 && this.labelmd == 0){
15175                 this.labelmd = this.labelWidth;
15176             }
15177             
15178             if(this.labellg > 0){
15179                 labelCfg.cls += ' col-lg-' + this.labellg;
15180                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15181             }
15182             
15183             if(this.labelmd > 0){
15184                 labelCfg.cls += ' col-md-' + this.labelmd;
15185                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15186             }
15187             
15188             if(this.labelsm > 0){
15189                 labelCfg.cls += ' col-sm-' + this.labelsm;
15190                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15191             }
15192             
15193             if(this.labelxs > 0){
15194                 labelCfg.cls += ' col-xs-' + this.labelxs;
15195                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15196             }
15197                 
15198                 
15199         } else if ( this.fieldLabel.length) {
15200             cfg.cn = [
15201                 {
15202                    tag : 'i',
15203                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15204                    tooltip : 'This field is required'
15205                 },
15206                 {
15207                     tag: 'label',
15208                     cls : 'control-label',
15209                     html : this.fieldLabel
15210
15211                 },
15212                 {
15213                     cls : '', 
15214                     cn: [
15215                         combobox
15216                     ]
15217                 }
15218             ];
15219             
15220             if(this.indicatorpos == 'right'){
15221                 cfg.cn = [
15222                     {
15223                         tag: 'label',
15224                         cls : 'control-label',
15225                         html : this.fieldLabel,
15226                         cn : [
15227                             {
15228                                tag : 'i',
15229                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15230                                tooltip : 'This field is required'
15231                             }
15232                         ]
15233                     },
15234                     {
15235                         cls : '', 
15236                         cn: [
15237                             combobox
15238                         ]
15239                     }
15240                 ];
15241             }
15242         } else {
15243             cfg.cn = combobox;    
15244         }
15245         
15246         
15247         var settings = this;
15248         
15249         ['xs','sm','md','lg'].map(function(size){
15250             if (settings[size]) {
15251                 cfg.cls += ' col-' + size + '-' + settings[size];
15252             }
15253         });
15254         
15255         return cfg;
15256     },
15257     
15258     initTouchView : function()
15259     {
15260         this.renderTouchView();
15261         
15262         this.touchViewEl.on('scroll', function(){
15263             this.el.dom.scrollTop = 0;
15264         }, this);
15265         
15266         this.originalValue = this.getValue();
15267         
15268         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15269         
15270         this.inputEl().on("click", this.showTouchView, this);
15271         if (this.triggerEl) {
15272             this.triggerEl.on("click", this.showTouchView, this);
15273         }
15274         
15275         
15276         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15277         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15278         
15279         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15280         
15281         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15282         this.store.on('load', this.onTouchViewLoad, this);
15283         this.store.on('loadexception', this.onTouchViewLoadException, this);
15284         
15285         if(this.hiddenName){
15286             
15287             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15288             
15289             this.hiddenField.dom.value =
15290                 this.hiddenValue !== undefined ? this.hiddenValue :
15291                 this.value !== undefined ? this.value : '';
15292         
15293             this.el.dom.removeAttribute('name');
15294             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15295         }
15296         
15297         if(this.multiple){
15298             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15299             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15300         }
15301         
15302         if(this.removable && !this.multiple){
15303             var close = this.closeTriggerEl();
15304             if(close){
15305                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15306                 close.on('click', this.removeBtnClick, this, close);
15307             }
15308         }
15309         /*
15310          * fix the bug in Safari iOS8
15311          */
15312         this.inputEl().on("focus", function(e){
15313             document.activeElement.blur();
15314         }, this);
15315         
15316         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15317         
15318         return;
15319         
15320         
15321     },
15322     
15323     renderTouchView : function()
15324     {
15325         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15326         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15327         
15328         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15329         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15330         
15331         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15332         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15333         this.touchViewBodyEl.setStyle('overflow', 'auto');
15334         
15335         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15336         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15337         
15338         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15339         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15340         
15341     },
15342     
15343     showTouchView : function()
15344     {
15345         if(this.disabled){
15346             return;
15347         }
15348         
15349         this.touchViewHeaderEl.hide();
15350
15351         if(this.modalTitle.length){
15352             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15353             this.touchViewHeaderEl.show();
15354         }
15355
15356         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15357         this.touchViewEl.show();
15358
15359         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15360         
15361         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15362         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15363
15364         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15365
15366         if(this.modalTitle.length){
15367             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15368         }
15369         
15370         this.touchViewBodyEl.setHeight(bodyHeight);
15371
15372         if(this.animate){
15373             var _this = this;
15374             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15375         }else{
15376             this.touchViewEl.addClass('in');
15377         }
15378         
15379         if(this._touchViewMask){
15380             Roo.get(document.body).addClass("x-body-masked");
15381             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15382             this._touchViewMask.setStyle('z-index', 10000);
15383             this._touchViewMask.addClass('show');
15384         }
15385         
15386         this.doTouchViewQuery();
15387         
15388     },
15389     
15390     hideTouchView : function()
15391     {
15392         this.touchViewEl.removeClass('in');
15393
15394         if(this.animate){
15395             var _this = this;
15396             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15397         }else{
15398             this.touchViewEl.setStyle('display', 'none');
15399         }
15400         
15401         if(this._touchViewMask){
15402             this._touchViewMask.removeClass('show');
15403             Roo.get(document.body).removeClass("x-body-masked");
15404         }
15405     },
15406     
15407     setTouchViewValue : function()
15408     {
15409         if(this.multiple){
15410             this.clearItem();
15411         
15412             var _this = this;
15413
15414             Roo.each(this.tickItems, function(o){
15415                 this.addItem(o);
15416             }, this);
15417         }
15418         
15419         this.hideTouchView();
15420     },
15421     
15422     doTouchViewQuery : function()
15423     {
15424         var qe = {
15425             query: '',
15426             forceAll: true,
15427             combo: this,
15428             cancel:false
15429         };
15430         
15431         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15432             return false;
15433         }
15434         
15435         if(!this.alwaysQuery || this.mode == 'local'){
15436             this.onTouchViewLoad();
15437             return;
15438         }
15439         
15440         this.store.load();
15441     },
15442     
15443     onTouchViewBeforeLoad : function(combo,opts)
15444     {
15445         return;
15446     },
15447
15448     // private
15449     onTouchViewLoad : function()
15450     {
15451         if(this.store.getCount() < 1){
15452             this.onTouchViewEmptyResults();
15453             return;
15454         }
15455         
15456         this.clearTouchView();
15457         
15458         var rawValue = this.getRawValue();
15459         
15460         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15461         
15462         this.tickItems = [];
15463         
15464         this.store.data.each(function(d, rowIndex){
15465             var row = this.touchViewListGroup.createChild(template);
15466             
15467             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15468                 row.addClass(d.data.cls);
15469             }
15470             
15471             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15472                 var cfg = {
15473                     data : d.data,
15474                     html : d.data[this.displayField]
15475                 };
15476                 
15477                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15478                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15479                 }
15480             }
15481             row.removeClass('selected');
15482             if(!this.multiple && this.valueField &&
15483                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15484             {
15485                 // radio buttons..
15486                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15487                 row.addClass('selected');
15488             }
15489             
15490             if(this.multiple && this.valueField &&
15491                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15492             {
15493                 
15494                 // checkboxes...
15495                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15496                 this.tickItems.push(d.data);
15497             }
15498             
15499             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15500             
15501         }, this);
15502         
15503         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15504         
15505         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15506
15507         if(this.modalTitle.length){
15508             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15509         }
15510
15511         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15512         
15513         if(this.mobile_restrict_height && listHeight < bodyHeight){
15514             this.touchViewBodyEl.setHeight(listHeight);
15515         }
15516         
15517         var _this = this;
15518         
15519         if(firstChecked && listHeight > bodyHeight){
15520             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15521         }
15522         
15523     },
15524     
15525     onTouchViewLoadException : function()
15526     {
15527         this.hideTouchView();
15528     },
15529     
15530     onTouchViewEmptyResults : function()
15531     {
15532         this.clearTouchView();
15533         
15534         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15535         
15536         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15537         
15538     },
15539     
15540     clearTouchView : function()
15541     {
15542         this.touchViewListGroup.dom.innerHTML = '';
15543     },
15544     
15545     onTouchViewClick : function(e, el, o)
15546     {
15547         e.preventDefault();
15548         
15549         var row = o.row;
15550         var rowIndex = o.rowIndex;
15551         
15552         var r = this.store.getAt(rowIndex);
15553         
15554         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15555             
15556             if(!this.multiple){
15557                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15558                     c.dom.removeAttribute('checked');
15559                 }, this);
15560
15561                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15562
15563                 this.setFromData(r.data);
15564
15565                 var close = this.closeTriggerEl();
15566
15567                 if(close){
15568                     close.show();
15569                 }
15570
15571                 this.hideTouchView();
15572
15573                 this.fireEvent('select', this, r, rowIndex);
15574
15575                 return;
15576             }
15577
15578             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15579                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15580                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15581                 return;
15582             }
15583
15584             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15585             this.addItem(r.data);
15586             this.tickItems.push(r.data);
15587         }
15588     },
15589     
15590     getAutoCreateNativeIOS : function()
15591     {
15592         var cfg = {
15593             cls: 'form-group' //input-group,
15594         };
15595         
15596         var combobox =  {
15597             tag: 'select',
15598             cls : 'roo-ios-select'
15599         };
15600         
15601         if (this.name) {
15602             combobox.name = this.name;
15603         }
15604         
15605         if (this.disabled) {
15606             combobox.disabled = true;
15607         }
15608         
15609         var settings = this;
15610         
15611         ['xs','sm','md','lg'].map(function(size){
15612             if (settings[size]) {
15613                 cfg.cls += ' col-' + size + '-' + settings[size];
15614             }
15615         });
15616         
15617         cfg.cn = combobox;
15618         
15619         return cfg;
15620         
15621     },
15622     
15623     initIOSView : function()
15624     {
15625         this.store.on('load', this.onIOSViewLoad, this);
15626         
15627         return;
15628     },
15629     
15630     onIOSViewLoad : function()
15631     {
15632         if(this.store.getCount() < 1){
15633             return;
15634         }
15635         
15636         this.clearIOSView();
15637         
15638         if(this.allowBlank) {
15639             
15640             var default_text = '-- SELECT --';
15641             
15642             if(this.placeholder.length){
15643                 default_text = this.placeholder;
15644             }
15645             
15646             if(this.emptyTitle.length){
15647                 default_text += ' - ' + this.emptyTitle + ' -';
15648             }
15649             
15650             var opt = this.inputEl().createChild({
15651                 tag: 'option',
15652                 value : 0,
15653                 html : default_text
15654             });
15655             
15656             var o = {};
15657             o[this.valueField] = 0;
15658             o[this.displayField] = default_text;
15659             
15660             this.ios_options.push({
15661                 data : o,
15662                 el : opt
15663             });
15664             
15665         }
15666         
15667         this.store.data.each(function(d, rowIndex){
15668             
15669             var html = '';
15670             
15671             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15672                 html = d.data[this.displayField];
15673             }
15674             
15675             var value = '';
15676             
15677             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15678                 value = d.data[this.valueField];
15679             }
15680             
15681             var option = {
15682                 tag: 'option',
15683                 value : value,
15684                 html : html
15685             };
15686             
15687             if(this.value == d.data[this.valueField]){
15688                 option['selected'] = true;
15689             }
15690             
15691             var opt = this.inputEl().createChild(option);
15692             
15693             this.ios_options.push({
15694                 data : d.data,
15695                 el : opt
15696             });
15697             
15698         }, this);
15699         
15700         this.inputEl().on('change', function(){
15701            this.fireEvent('select', this);
15702         }, this);
15703         
15704     },
15705     
15706     clearIOSView: function()
15707     {
15708         this.inputEl().dom.innerHTML = '';
15709         
15710         this.ios_options = [];
15711     },
15712     
15713     setIOSValue: function(v)
15714     {
15715         this.value = v;
15716         
15717         if(!this.ios_options){
15718             return;
15719         }
15720         
15721         Roo.each(this.ios_options, function(opts){
15722            
15723            opts.el.dom.removeAttribute('selected');
15724            
15725            if(opts.data[this.valueField] != v){
15726                return;
15727            }
15728            
15729            opts.el.dom.setAttribute('selected', true);
15730            
15731         }, this);
15732     }
15733
15734     /** 
15735     * @cfg {Boolean} grow 
15736     * @hide 
15737     */
15738     /** 
15739     * @cfg {Number} growMin 
15740     * @hide 
15741     */
15742     /** 
15743     * @cfg {Number} growMax 
15744     * @hide 
15745     */
15746     /**
15747      * @hide
15748      * @method autoSize
15749      */
15750 });
15751
15752 Roo.apply(Roo.bootstrap.ComboBox,  {
15753     
15754     header : {
15755         tag: 'div',
15756         cls: 'modal-header',
15757         cn: [
15758             {
15759                 tag: 'h4',
15760                 cls: 'modal-title'
15761             }
15762         ]
15763     },
15764     
15765     body : {
15766         tag: 'div',
15767         cls: 'modal-body',
15768         cn: [
15769             {
15770                 tag: 'ul',
15771                 cls: 'list-group'
15772             }
15773         ]
15774     },
15775     
15776     listItemRadio : {
15777         tag: 'li',
15778         cls: 'list-group-item',
15779         cn: [
15780             {
15781                 tag: 'span',
15782                 cls: 'roo-combobox-list-group-item-value'
15783             },
15784             {
15785                 tag: 'div',
15786                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15787                 cn: [
15788                     {
15789                         tag: 'input',
15790                         type: 'radio'
15791                     },
15792                     {
15793                         tag: 'label'
15794                     }
15795                 ]
15796             }
15797         ]
15798     },
15799     
15800     listItemCheckbox : {
15801         tag: 'li',
15802         cls: 'list-group-item',
15803         cn: [
15804             {
15805                 tag: 'span',
15806                 cls: 'roo-combobox-list-group-item-value'
15807             },
15808             {
15809                 tag: 'div',
15810                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15811                 cn: [
15812                     {
15813                         tag: 'input',
15814                         type: 'checkbox'
15815                     },
15816                     {
15817                         tag: 'label'
15818                     }
15819                 ]
15820             }
15821         ]
15822     },
15823     
15824     emptyResult : {
15825         tag: 'div',
15826         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15827     },
15828     
15829     footer : {
15830         tag: 'div',
15831         cls: 'modal-footer',
15832         cn: [
15833             {
15834                 tag: 'div',
15835                 cls: 'row',
15836                 cn: [
15837                     {
15838                         tag: 'div',
15839                         cls: 'col-xs-6 text-left',
15840                         cn: {
15841                             tag: 'button',
15842                             cls: 'btn btn-danger roo-touch-view-cancel',
15843                             html: 'Cancel'
15844                         }
15845                     },
15846                     {
15847                         tag: 'div',
15848                         cls: 'col-xs-6 text-right',
15849                         cn: {
15850                             tag: 'button',
15851                             cls: 'btn btn-success roo-touch-view-ok',
15852                             html: 'OK'
15853                         }
15854                     }
15855                 ]
15856             }
15857         ]
15858         
15859     }
15860 });
15861
15862 Roo.apply(Roo.bootstrap.ComboBox,  {
15863     
15864     touchViewTemplate : {
15865         tag: 'div',
15866         cls: 'modal fade roo-combobox-touch-view',
15867         cn: [
15868             {
15869                 tag: 'div',
15870                 cls: 'modal-dialog',
15871                 style : 'position:fixed', // we have to fix position....
15872                 cn: [
15873                     {
15874                         tag: 'div',
15875                         cls: 'modal-content',
15876                         cn: [
15877                             Roo.bootstrap.ComboBox.header,
15878                             Roo.bootstrap.ComboBox.body,
15879                             Roo.bootstrap.ComboBox.footer
15880                         ]
15881                     }
15882                 ]
15883             }
15884         ]
15885     }
15886 });/*
15887  * Based on:
15888  * Ext JS Library 1.1.1
15889  * Copyright(c) 2006-2007, Ext JS, LLC.
15890  *
15891  * Originally Released Under LGPL - original licence link has changed is not relivant.
15892  *
15893  * Fork - LGPL
15894  * <script type="text/javascript">
15895  */
15896
15897 /**
15898  * @class Roo.View
15899  * @extends Roo.util.Observable
15900  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15901  * This class also supports single and multi selection modes. <br>
15902  * Create a data model bound view:
15903  <pre><code>
15904  var store = new Roo.data.Store(...);
15905
15906  var view = new Roo.View({
15907     el : "my-element",
15908     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15909  
15910     singleSelect: true,
15911     selectedClass: "ydataview-selected",
15912     store: store
15913  });
15914
15915  // listen for node click?
15916  view.on("click", function(vw, index, node, e){
15917  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15918  });
15919
15920  // load XML data
15921  dataModel.load("foobar.xml");
15922  </code></pre>
15923  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15924  * <br><br>
15925  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15926  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15927  * 
15928  * Note: old style constructor is still suported (container, template, config)
15929  * 
15930  * @constructor
15931  * Create a new View
15932  * @param {Object} config The config object
15933  * 
15934  */
15935 Roo.View = function(config, depreciated_tpl, depreciated_config){
15936     
15937     this.parent = false;
15938     
15939     if (typeof(depreciated_tpl) == 'undefined') {
15940         // new way.. - universal constructor.
15941         Roo.apply(this, config);
15942         this.el  = Roo.get(this.el);
15943     } else {
15944         // old format..
15945         this.el  = Roo.get(config);
15946         this.tpl = depreciated_tpl;
15947         Roo.apply(this, depreciated_config);
15948     }
15949     this.wrapEl  = this.el.wrap().wrap();
15950     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15951     
15952     
15953     if(typeof(this.tpl) == "string"){
15954         this.tpl = new Roo.Template(this.tpl);
15955     } else {
15956         // support xtype ctors..
15957         this.tpl = new Roo.factory(this.tpl, Roo);
15958     }
15959     
15960     
15961     this.tpl.compile();
15962     
15963     /** @private */
15964     this.addEvents({
15965         /**
15966          * @event beforeclick
15967          * Fires before a click is processed. Returns false to cancel the default action.
15968          * @param {Roo.View} this
15969          * @param {Number} index The index of the target node
15970          * @param {HTMLElement} node The target node
15971          * @param {Roo.EventObject} e The raw event object
15972          */
15973             "beforeclick" : true,
15974         /**
15975          * @event click
15976          * Fires when a template node is clicked.
15977          * @param {Roo.View} this
15978          * @param {Number} index The index of the target node
15979          * @param {HTMLElement} node The target node
15980          * @param {Roo.EventObject} e The raw event object
15981          */
15982             "click" : true,
15983         /**
15984          * @event dblclick
15985          * Fires when a template node is double clicked.
15986          * @param {Roo.View} this
15987          * @param {Number} index The index of the target node
15988          * @param {HTMLElement} node The target node
15989          * @param {Roo.EventObject} e The raw event object
15990          */
15991             "dblclick" : true,
15992         /**
15993          * @event contextmenu
15994          * Fires when a template node is right clicked.
15995          * @param {Roo.View} this
15996          * @param {Number} index The index of the target node
15997          * @param {HTMLElement} node The target node
15998          * @param {Roo.EventObject} e The raw event object
15999          */
16000             "contextmenu" : true,
16001         /**
16002          * @event selectionchange
16003          * Fires when the selected nodes change.
16004          * @param {Roo.View} this
16005          * @param {Array} selections Array of the selected nodes
16006          */
16007             "selectionchange" : true,
16008     
16009         /**
16010          * @event beforeselect
16011          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16012          * @param {Roo.View} this
16013          * @param {HTMLElement} node The node to be selected
16014          * @param {Array} selections Array of currently selected nodes
16015          */
16016             "beforeselect" : true,
16017         /**
16018          * @event preparedata
16019          * Fires on every row to render, to allow you to change the data.
16020          * @param {Roo.View} this
16021          * @param {Object} data to be rendered (change this)
16022          */
16023           "preparedata" : true
16024           
16025           
16026         });
16027
16028
16029
16030     this.el.on({
16031         "click": this.onClick,
16032         "dblclick": this.onDblClick,
16033         "contextmenu": this.onContextMenu,
16034         scope:this
16035     });
16036
16037     this.selections = [];
16038     this.nodes = [];
16039     this.cmp = new Roo.CompositeElementLite([]);
16040     if(this.store){
16041         this.store = Roo.factory(this.store, Roo.data);
16042         this.setStore(this.store, true);
16043     }
16044     
16045     if ( this.footer && this.footer.xtype) {
16046            
16047          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16048         
16049         this.footer.dataSource = this.store;
16050         this.footer.container = fctr;
16051         this.footer = Roo.factory(this.footer, Roo);
16052         fctr.insertFirst(this.el);
16053         
16054         // this is a bit insane - as the paging toolbar seems to detach the el..
16055 //        dom.parentNode.parentNode.parentNode
16056          // they get detached?
16057     }
16058     
16059     
16060     Roo.View.superclass.constructor.call(this);
16061     
16062     
16063 };
16064
16065 Roo.extend(Roo.View, Roo.util.Observable, {
16066     
16067      /**
16068      * @cfg {Roo.data.Store} store Data store to load data from.
16069      */
16070     store : false,
16071     
16072     /**
16073      * @cfg {String|Roo.Element} el The container element.
16074      */
16075     el : '',
16076     
16077     /**
16078      * @cfg {String|Roo.Template} tpl The template used by this View 
16079      */
16080     tpl : false,
16081     /**
16082      * @cfg {String} dataName the named area of the template to use as the data area
16083      *                          Works with domtemplates roo-name="name"
16084      */
16085     dataName: false,
16086     /**
16087      * @cfg {String} selectedClass The css class to add to selected nodes
16088      */
16089     selectedClass : "x-view-selected",
16090      /**
16091      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16092      */
16093     emptyText : "",
16094     
16095     /**
16096      * @cfg {String} text to display on mask (default Loading)
16097      */
16098     mask : false,
16099     /**
16100      * @cfg {Boolean} multiSelect Allow multiple selection
16101      */
16102     multiSelect : false,
16103     /**
16104      * @cfg {Boolean} singleSelect Allow single selection
16105      */
16106     singleSelect:  false,
16107     
16108     /**
16109      * @cfg {Boolean} toggleSelect - selecting 
16110      */
16111     toggleSelect : false,
16112     
16113     /**
16114      * @cfg {Boolean} tickable - selecting 
16115      */
16116     tickable : false,
16117     
16118     /**
16119      * Returns the element this view is bound to.
16120      * @return {Roo.Element}
16121      */
16122     getEl : function(){
16123         return this.wrapEl;
16124     },
16125     
16126     
16127
16128     /**
16129      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16130      */
16131     refresh : function(){
16132         //Roo.log('refresh');
16133         var t = this.tpl;
16134         
16135         // if we are using something like 'domtemplate', then
16136         // the what gets used is:
16137         // t.applySubtemplate(NAME, data, wrapping data..)
16138         // the outer template then get' applied with
16139         //     the store 'extra data'
16140         // and the body get's added to the
16141         //      roo-name="data" node?
16142         //      <span class='roo-tpl-{name}'></span> ?????
16143         
16144         
16145         
16146         this.clearSelections();
16147         this.el.update("");
16148         var html = [];
16149         var records = this.store.getRange();
16150         if(records.length < 1) {
16151             
16152             // is this valid??  = should it render a template??
16153             
16154             this.el.update(this.emptyText);
16155             return;
16156         }
16157         var el = this.el;
16158         if (this.dataName) {
16159             this.el.update(t.apply(this.store.meta)); //????
16160             el = this.el.child('.roo-tpl-' + this.dataName);
16161         }
16162         
16163         for(var i = 0, len = records.length; i < len; i++){
16164             var data = this.prepareData(records[i].data, i, records[i]);
16165             this.fireEvent("preparedata", this, data, i, records[i]);
16166             
16167             var d = Roo.apply({}, data);
16168             
16169             if(this.tickable){
16170                 Roo.apply(d, {'roo-id' : Roo.id()});
16171                 
16172                 var _this = this;
16173             
16174                 Roo.each(this.parent.item, function(item){
16175                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16176                         return;
16177                     }
16178                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16179                 });
16180             }
16181             
16182             html[html.length] = Roo.util.Format.trim(
16183                 this.dataName ?
16184                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16185                     t.apply(d)
16186             );
16187         }
16188         
16189         
16190         
16191         el.update(html.join(""));
16192         this.nodes = el.dom.childNodes;
16193         this.updateIndexes(0);
16194     },
16195     
16196
16197     /**
16198      * Function to override to reformat the data that is sent to
16199      * the template for each node.
16200      * DEPRICATED - use the preparedata event handler.
16201      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16202      * a JSON object for an UpdateManager bound view).
16203      */
16204     prepareData : function(data, index, record)
16205     {
16206         this.fireEvent("preparedata", this, data, index, record);
16207         return data;
16208     },
16209
16210     onUpdate : function(ds, record){
16211         // Roo.log('on update');   
16212         this.clearSelections();
16213         var index = this.store.indexOf(record);
16214         var n = this.nodes[index];
16215         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16216         n.parentNode.removeChild(n);
16217         this.updateIndexes(index, index);
16218     },
16219
16220     
16221     
16222 // --------- FIXME     
16223     onAdd : function(ds, records, index)
16224     {
16225         //Roo.log(['on Add', ds, records, index] );        
16226         this.clearSelections();
16227         if(this.nodes.length == 0){
16228             this.refresh();
16229             return;
16230         }
16231         var n = this.nodes[index];
16232         for(var i = 0, len = records.length; i < len; i++){
16233             var d = this.prepareData(records[i].data, i, records[i]);
16234             if(n){
16235                 this.tpl.insertBefore(n, d);
16236             }else{
16237                 
16238                 this.tpl.append(this.el, d);
16239             }
16240         }
16241         this.updateIndexes(index);
16242     },
16243
16244     onRemove : function(ds, record, index){
16245        // Roo.log('onRemove');
16246         this.clearSelections();
16247         var el = this.dataName  ?
16248             this.el.child('.roo-tpl-' + this.dataName) :
16249             this.el; 
16250         
16251         el.dom.removeChild(this.nodes[index]);
16252         this.updateIndexes(index);
16253     },
16254
16255     /**
16256      * Refresh an individual node.
16257      * @param {Number} index
16258      */
16259     refreshNode : function(index){
16260         this.onUpdate(this.store, this.store.getAt(index));
16261     },
16262
16263     updateIndexes : function(startIndex, endIndex){
16264         var ns = this.nodes;
16265         startIndex = startIndex || 0;
16266         endIndex = endIndex || ns.length - 1;
16267         for(var i = startIndex; i <= endIndex; i++){
16268             ns[i].nodeIndex = i;
16269         }
16270     },
16271
16272     /**
16273      * Changes the data store this view uses and refresh the view.
16274      * @param {Store} store
16275      */
16276     setStore : function(store, initial){
16277         if(!initial && this.store){
16278             this.store.un("datachanged", this.refresh);
16279             this.store.un("add", this.onAdd);
16280             this.store.un("remove", this.onRemove);
16281             this.store.un("update", this.onUpdate);
16282             this.store.un("clear", this.refresh);
16283             this.store.un("beforeload", this.onBeforeLoad);
16284             this.store.un("load", this.onLoad);
16285             this.store.un("loadexception", this.onLoad);
16286         }
16287         if(store){
16288           
16289             store.on("datachanged", this.refresh, this);
16290             store.on("add", this.onAdd, this);
16291             store.on("remove", this.onRemove, this);
16292             store.on("update", this.onUpdate, this);
16293             store.on("clear", this.refresh, this);
16294             store.on("beforeload", this.onBeforeLoad, this);
16295             store.on("load", this.onLoad, this);
16296             store.on("loadexception", this.onLoad, this);
16297         }
16298         
16299         if(store){
16300             this.refresh();
16301         }
16302     },
16303     /**
16304      * onbeforeLoad - masks the loading area.
16305      *
16306      */
16307     onBeforeLoad : function(store,opts)
16308     {
16309          //Roo.log('onBeforeLoad');   
16310         if (!opts.add) {
16311             this.el.update("");
16312         }
16313         this.el.mask(this.mask ? this.mask : "Loading" ); 
16314     },
16315     onLoad : function ()
16316     {
16317         this.el.unmask();
16318     },
16319     
16320
16321     /**
16322      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16323      * @param {HTMLElement} node
16324      * @return {HTMLElement} The template node
16325      */
16326     findItemFromChild : function(node){
16327         var el = this.dataName  ?
16328             this.el.child('.roo-tpl-' + this.dataName,true) :
16329             this.el.dom; 
16330         
16331         if(!node || node.parentNode == el){
16332                     return node;
16333             }
16334             var p = node.parentNode;
16335             while(p && p != el){
16336             if(p.parentNode == el){
16337                 return p;
16338             }
16339             p = p.parentNode;
16340         }
16341             return null;
16342     },
16343
16344     /** @ignore */
16345     onClick : function(e){
16346         var item = this.findItemFromChild(e.getTarget());
16347         if(item){
16348             var index = this.indexOf(item);
16349             if(this.onItemClick(item, index, e) !== false){
16350                 this.fireEvent("click", this, index, item, e);
16351             }
16352         }else{
16353             this.clearSelections();
16354         }
16355     },
16356
16357     /** @ignore */
16358     onContextMenu : function(e){
16359         var item = this.findItemFromChild(e.getTarget());
16360         if(item){
16361             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16362         }
16363     },
16364
16365     /** @ignore */
16366     onDblClick : function(e){
16367         var item = this.findItemFromChild(e.getTarget());
16368         if(item){
16369             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16370         }
16371     },
16372
16373     onItemClick : function(item, index, e)
16374     {
16375         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16376             return false;
16377         }
16378         if (this.toggleSelect) {
16379             var m = this.isSelected(item) ? 'unselect' : 'select';
16380             //Roo.log(m);
16381             var _t = this;
16382             _t[m](item, true, false);
16383             return true;
16384         }
16385         if(this.multiSelect || this.singleSelect){
16386             if(this.multiSelect && e.shiftKey && this.lastSelection){
16387                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16388             }else{
16389                 this.select(item, this.multiSelect && e.ctrlKey);
16390                 this.lastSelection = item;
16391             }
16392             
16393             if(!this.tickable){
16394                 e.preventDefault();
16395             }
16396             
16397         }
16398         return true;
16399     },
16400
16401     /**
16402      * Get the number of selected nodes.
16403      * @return {Number}
16404      */
16405     getSelectionCount : function(){
16406         return this.selections.length;
16407     },
16408
16409     /**
16410      * Get the currently selected nodes.
16411      * @return {Array} An array of HTMLElements
16412      */
16413     getSelectedNodes : function(){
16414         return this.selections;
16415     },
16416
16417     /**
16418      * Get the indexes of the selected nodes.
16419      * @return {Array}
16420      */
16421     getSelectedIndexes : function(){
16422         var indexes = [], s = this.selections;
16423         for(var i = 0, len = s.length; i < len; i++){
16424             indexes.push(s[i].nodeIndex);
16425         }
16426         return indexes;
16427     },
16428
16429     /**
16430      * Clear all selections
16431      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16432      */
16433     clearSelections : function(suppressEvent){
16434         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16435             this.cmp.elements = this.selections;
16436             this.cmp.removeClass(this.selectedClass);
16437             this.selections = [];
16438             if(!suppressEvent){
16439                 this.fireEvent("selectionchange", this, this.selections);
16440             }
16441         }
16442     },
16443
16444     /**
16445      * Returns true if the passed node is selected
16446      * @param {HTMLElement/Number} node The node or node index
16447      * @return {Boolean}
16448      */
16449     isSelected : function(node){
16450         var s = this.selections;
16451         if(s.length < 1){
16452             return false;
16453         }
16454         node = this.getNode(node);
16455         return s.indexOf(node) !== -1;
16456     },
16457
16458     /**
16459      * Selects nodes.
16460      * @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
16461      * @param {Boolean} keepExisting (optional) true to keep existing selections
16462      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16463      */
16464     select : function(nodeInfo, keepExisting, suppressEvent){
16465         if(nodeInfo instanceof Array){
16466             if(!keepExisting){
16467                 this.clearSelections(true);
16468             }
16469             for(var i = 0, len = nodeInfo.length; i < len; i++){
16470                 this.select(nodeInfo[i], true, true);
16471             }
16472             return;
16473         } 
16474         var node = this.getNode(nodeInfo);
16475         if(!node || this.isSelected(node)){
16476             return; // already selected.
16477         }
16478         if(!keepExisting){
16479             this.clearSelections(true);
16480         }
16481         
16482         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16483             Roo.fly(node).addClass(this.selectedClass);
16484             this.selections.push(node);
16485             if(!suppressEvent){
16486                 this.fireEvent("selectionchange", this, this.selections);
16487             }
16488         }
16489         
16490         
16491     },
16492       /**
16493      * Unselects nodes.
16494      * @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
16495      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16496      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16497      */
16498     unselect : function(nodeInfo, keepExisting, suppressEvent)
16499     {
16500         if(nodeInfo instanceof Array){
16501             Roo.each(this.selections, function(s) {
16502                 this.unselect(s, nodeInfo);
16503             }, this);
16504             return;
16505         }
16506         var node = this.getNode(nodeInfo);
16507         if(!node || !this.isSelected(node)){
16508             //Roo.log("not selected");
16509             return; // not selected.
16510         }
16511         // fireevent???
16512         var ns = [];
16513         Roo.each(this.selections, function(s) {
16514             if (s == node ) {
16515                 Roo.fly(node).removeClass(this.selectedClass);
16516
16517                 return;
16518             }
16519             ns.push(s);
16520         },this);
16521         
16522         this.selections= ns;
16523         this.fireEvent("selectionchange", this, this.selections);
16524     },
16525
16526     /**
16527      * Gets a template node.
16528      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16529      * @return {HTMLElement} The node or null if it wasn't found
16530      */
16531     getNode : function(nodeInfo){
16532         if(typeof nodeInfo == "string"){
16533             return document.getElementById(nodeInfo);
16534         }else if(typeof nodeInfo == "number"){
16535             return this.nodes[nodeInfo];
16536         }
16537         return nodeInfo;
16538     },
16539
16540     /**
16541      * Gets a range template nodes.
16542      * @param {Number} startIndex
16543      * @param {Number} endIndex
16544      * @return {Array} An array of nodes
16545      */
16546     getNodes : function(start, end){
16547         var ns = this.nodes;
16548         start = start || 0;
16549         end = typeof end == "undefined" ? ns.length - 1 : end;
16550         var nodes = [];
16551         if(start <= end){
16552             for(var i = start; i <= end; i++){
16553                 nodes.push(ns[i]);
16554             }
16555         } else{
16556             for(var i = start; i >= end; i--){
16557                 nodes.push(ns[i]);
16558             }
16559         }
16560         return nodes;
16561     },
16562
16563     /**
16564      * Finds the index of the passed node
16565      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16566      * @return {Number} The index of the node or -1
16567      */
16568     indexOf : function(node){
16569         node = this.getNode(node);
16570         if(typeof node.nodeIndex == "number"){
16571             return node.nodeIndex;
16572         }
16573         var ns = this.nodes;
16574         for(var i = 0, len = ns.length; i < len; i++){
16575             if(ns[i] == node){
16576                 return i;
16577             }
16578         }
16579         return -1;
16580     }
16581 });
16582 /*
16583  * - LGPL
16584  *
16585  * based on jquery fullcalendar
16586  * 
16587  */
16588
16589 Roo.bootstrap = Roo.bootstrap || {};
16590 /**
16591  * @class Roo.bootstrap.Calendar
16592  * @extends Roo.bootstrap.Component
16593  * Bootstrap Calendar class
16594  * @cfg {Boolean} loadMask (true|false) default false
16595  * @cfg {Object} header generate the user specific header of the calendar, default false
16596
16597  * @constructor
16598  * Create a new Container
16599  * @param {Object} config The config object
16600  */
16601
16602
16603
16604 Roo.bootstrap.Calendar = function(config){
16605     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16606      this.addEvents({
16607         /**
16608              * @event select
16609              * Fires when a date is selected
16610              * @param {DatePicker} this
16611              * @param {Date} date The selected date
16612              */
16613         'select': true,
16614         /**
16615              * @event monthchange
16616              * Fires when the displayed month changes 
16617              * @param {DatePicker} this
16618              * @param {Date} date The selected month
16619              */
16620         'monthchange': true,
16621         /**
16622              * @event evententer
16623              * Fires when mouse over an event
16624              * @param {Calendar} this
16625              * @param {event} Event
16626              */
16627         'evententer': true,
16628         /**
16629              * @event eventleave
16630              * Fires when the mouse leaves an
16631              * @param {Calendar} this
16632              * @param {event}
16633              */
16634         'eventleave': true,
16635         /**
16636              * @event eventclick
16637              * Fires when the mouse click an
16638              * @param {Calendar} this
16639              * @param {event}
16640              */
16641         'eventclick': true
16642         
16643     });
16644
16645 };
16646
16647 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16648     
16649      /**
16650      * @cfg {Number} startDay
16651      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16652      */
16653     startDay : 0,
16654     
16655     loadMask : false,
16656     
16657     header : false,
16658       
16659     getAutoCreate : function(){
16660         
16661         
16662         var fc_button = function(name, corner, style, content ) {
16663             return Roo.apply({},{
16664                 tag : 'span',
16665                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16666                          (corner.length ?
16667                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16668                             ''
16669                         ),
16670                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16671                 unselectable: 'on'
16672             });
16673         };
16674         
16675         var header = {};
16676         
16677         if(!this.header){
16678             header = {
16679                 tag : 'table',
16680                 cls : 'fc-header',
16681                 style : 'width:100%',
16682                 cn : [
16683                     {
16684                         tag: 'tr',
16685                         cn : [
16686                             {
16687                                 tag : 'td',
16688                                 cls : 'fc-header-left',
16689                                 cn : [
16690                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16691                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16692                                     { tag: 'span', cls: 'fc-header-space' },
16693                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16694
16695
16696                                 ]
16697                             },
16698
16699                             {
16700                                 tag : 'td',
16701                                 cls : 'fc-header-center',
16702                                 cn : [
16703                                     {
16704                                         tag: 'span',
16705                                         cls: 'fc-header-title',
16706                                         cn : {
16707                                             tag: 'H2',
16708                                             html : 'month / year'
16709                                         }
16710                                     }
16711
16712                                 ]
16713                             },
16714                             {
16715                                 tag : 'td',
16716                                 cls : 'fc-header-right',
16717                                 cn : [
16718                               /*      fc_button('month', 'left', '', 'month' ),
16719                                     fc_button('week', '', '', 'week' ),
16720                                     fc_button('day', 'right', '', 'day' )
16721                                 */    
16722
16723                                 ]
16724                             }
16725
16726                         ]
16727                     }
16728                 ]
16729             };
16730         }
16731         
16732         header = this.header;
16733         
16734        
16735         var cal_heads = function() {
16736             var ret = [];
16737             // fixme - handle this.
16738             
16739             for (var i =0; i < Date.dayNames.length; i++) {
16740                 var d = Date.dayNames[i];
16741                 ret.push({
16742                     tag: 'th',
16743                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16744                     html : d.substring(0,3)
16745                 });
16746                 
16747             }
16748             ret[0].cls += ' fc-first';
16749             ret[6].cls += ' fc-last';
16750             return ret;
16751         };
16752         var cal_cell = function(n) {
16753             return  {
16754                 tag: 'td',
16755                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16756                 cn : [
16757                     {
16758                         cn : [
16759                             {
16760                                 cls: 'fc-day-number',
16761                                 html: 'D'
16762                             },
16763                             {
16764                                 cls: 'fc-day-content',
16765                              
16766                                 cn : [
16767                                      {
16768                                         style: 'position: relative;' // height: 17px;
16769                                     }
16770                                 ]
16771                             }
16772                             
16773                             
16774                         ]
16775                     }
16776                 ]
16777                 
16778             }
16779         };
16780         var cal_rows = function() {
16781             
16782             var ret = [];
16783             for (var r = 0; r < 6; r++) {
16784                 var row= {
16785                     tag : 'tr',
16786                     cls : 'fc-week',
16787                     cn : []
16788                 };
16789                 
16790                 for (var i =0; i < Date.dayNames.length; i++) {
16791                     var d = Date.dayNames[i];
16792                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16793
16794                 }
16795                 row.cn[0].cls+=' fc-first';
16796                 row.cn[0].cn[0].style = 'min-height:90px';
16797                 row.cn[6].cls+=' fc-last';
16798                 ret.push(row);
16799                 
16800             }
16801             ret[0].cls += ' fc-first';
16802             ret[4].cls += ' fc-prev-last';
16803             ret[5].cls += ' fc-last';
16804             return ret;
16805             
16806         };
16807         
16808         var cal_table = {
16809             tag: 'table',
16810             cls: 'fc-border-separate',
16811             style : 'width:100%',
16812             cellspacing  : 0,
16813             cn : [
16814                 { 
16815                     tag: 'thead',
16816                     cn : [
16817                         { 
16818                             tag: 'tr',
16819                             cls : 'fc-first fc-last',
16820                             cn : cal_heads()
16821                         }
16822                     ]
16823                 },
16824                 { 
16825                     tag: 'tbody',
16826                     cn : cal_rows()
16827                 }
16828                   
16829             ]
16830         };
16831          
16832          var cfg = {
16833             cls : 'fc fc-ltr',
16834             cn : [
16835                 header,
16836                 {
16837                     cls : 'fc-content',
16838                     style : "position: relative;",
16839                     cn : [
16840                         {
16841                             cls : 'fc-view fc-view-month fc-grid',
16842                             style : 'position: relative',
16843                             unselectable : 'on',
16844                             cn : [
16845                                 {
16846                                     cls : 'fc-event-container',
16847                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16848                                 },
16849                                 cal_table
16850                             ]
16851                         }
16852                     ]
16853     
16854                 }
16855            ] 
16856             
16857         };
16858         
16859          
16860         
16861         return cfg;
16862     },
16863     
16864     
16865     initEvents : function()
16866     {
16867         if(!this.store){
16868             throw "can not find store for calendar";
16869         }
16870         
16871         var mark = {
16872             tag: "div",
16873             cls:"x-dlg-mask",
16874             style: "text-align:center",
16875             cn: [
16876                 {
16877                     tag: "div",
16878                     style: "background-color:white;width:50%;margin:250 auto",
16879                     cn: [
16880                         {
16881                             tag: "img",
16882                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16883                         },
16884                         {
16885                             tag: "span",
16886                             html: "Loading"
16887                         }
16888                         
16889                     ]
16890                 }
16891             ]
16892         };
16893         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16894         
16895         var size = this.el.select('.fc-content', true).first().getSize();
16896         this.maskEl.setSize(size.width, size.height);
16897         this.maskEl.enableDisplayMode("block");
16898         if(!this.loadMask){
16899             this.maskEl.hide();
16900         }
16901         
16902         this.store = Roo.factory(this.store, Roo.data);
16903         this.store.on('load', this.onLoad, this);
16904         this.store.on('beforeload', this.onBeforeLoad, this);
16905         
16906         this.resize();
16907         
16908         this.cells = this.el.select('.fc-day',true);
16909         //Roo.log(this.cells);
16910         this.textNodes = this.el.query('.fc-day-number');
16911         this.cells.addClassOnOver('fc-state-hover');
16912         
16913         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16914         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16915         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16916         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16917         
16918         this.on('monthchange', this.onMonthChange, this);
16919         
16920         this.update(new Date().clearTime());
16921     },
16922     
16923     resize : function() {
16924         var sz  = this.el.getSize();
16925         
16926         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16927         this.el.select('.fc-day-content div',true).setHeight(34);
16928     },
16929     
16930     
16931     // private
16932     showPrevMonth : function(e){
16933         this.update(this.activeDate.add("mo", -1));
16934     },
16935     showToday : function(e){
16936         this.update(new Date().clearTime());
16937     },
16938     // private
16939     showNextMonth : function(e){
16940         this.update(this.activeDate.add("mo", 1));
16941     },
16942
16943     // private
16944     showPrevYear : function(){
16945         this.update(this.activeDate.add("y", -1));
16946     },
16947
16948     // private
16949     showNextYear : function(){
16950         this.update(this.activeDate.add("y", 1));
16951     },
16952
16953     
16954    // private
16955     update : function(date)
16956     {
16957         var vd = this.activeDate;
16958         this.activeDate = date;
16959 //        if(vd && this.el){
16960 //            var t = date.getTime();
16961 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16962 //                Roo.log('using add remove');
16963 //                
16964 //                this.fireEvent('monthchange', this, date);
16965 //                
16966 //                this.cells.removeClass("fc-state-highlight");
16967 //                this.cells.each(function(c){
16968 //                   if(c.dateValue == t){
16969 //                       c.addClass("fc-state-highlight");
16970 //                       setTimeout(function(){
16971 //                            try{c.dom.firstChild.focus();}catch(e){}
16972 //                       }, 50);
16973 //                       return false;
16974 //                   }
16975 //                   return true;
16976 //                });
16977 //                return;
16978 //            }
16979 //        }
16980         
16981         var days = date.getDaysInMonth();
16982         
16983         var firstOfMonth = date.getFirstDateOfMonth();
16984         var startingPos = firstOfMonth.getDay()-this.startDay;
16985         
16986         if(startingPos < this.startDay){
16987             startingPos += 7;
16988         }
16989         
16990         var pm = date.add(Date.MONTH, -1);
16991         var prevStart = pm.getDaysInMonth()-startingPos;
16992 //        
16993         this.cells = this.el.select('.fc-day',true);
16994         this.textNodes = this.el.query('.fc-day-number');
16995         this.cells.addClassOnOver('fc-state-hover');
16996         
16997         var cells = this.cells.elements;
16998         var textEls = this.textNodes;
16999         
17000         Roo.each(cells, function(cell){
17001             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17002         });
17003         
17004         days += startingPos;
17005
17006         // convert everything to numbers so it's fast
17007         var day = 86400000;
17008         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17009         //Roo.log(d);
17010         //Roo.log(pm);
17011         //Roo.log(prevStart);
17012         
17013         var today = new Date().clearTime().getTime();
17014         var sel = date.clearTime().getTime();
17015         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17016         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17017         var ddMatch = this.disabledDatesRE;
17018         var ddText = this.disabledDatesText;
17019         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17020         var ddaysText = this.disabledDaysText;
17021         var format = this.format;
17022         
17023         var setCellClass = function(cal, cell){
17024             cell.row = 0;
17025             cell.events = [];
17026             cell.more = [];
17027             //Roo.log('set Cell Class');
17028             cell.title = "";
17029             var t = d.getTime();
17030             
17031             //Roo.log(d);
17032             
17033             cell.dateValue = t;
17034             if(t == today){
17035                 cell.className += " fc-today";
17036                 cell.className += " fc-state-highlight";
17037                 cell.title = cal.todayText;
17038             }
17039             if(t == sel){
17040                 // disable highlight in other month..
17041                 //cell.className += " fc-state-highlight";
17042                 
17043             }
17044             // disabling
17045             if(t < min) {
17046                 cell.className = " fc-state-disabled";
17047                 cell.title = cal.minText;
17048                 return;
17049             }
17050             if(t > max) {
17051                 cell.className = " fc-state-disabled";
17052                 cell.title = cal.maxText;
17053                 return;
17054             }
17055             if(ddays){
17056                 if(ddays.indexOf(d.getDay()) != -1){
17057                     cell.title = ddaysText;
17058                     cell.className = " fc-state-disabled";
17059                 }
17060             }
17061             if(ddMatch && format){
17062                 var fvalue = d.dateFormat(format);
17063                 if(ddMatch.test(fvalue)){
17064                     cell.title = ddText.replace("%0", fvalue);
17065                     cell.className = " fc-state-disabled";
17066                 }
17067             }
17068             
17069             if (!cell.initialClassName) {
17070                 cell.initialClassName = cell.dom.className;
17071             }
17072             
17073             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17074         };
17075
17076         var i = 0;
17077         
17078         for(; i < startingPos; i++) {
17079             textEls[i].innerHTML = (++prevStart);
17080             d.setDate(d.getDate()+1);
17081             
17082             cells[i].className = "fc-past fc-other-month";
17083             setCellClass(this, cells[i]);
17084         }
17085         
17086         var intDay = 0;
17087         
17088         for(; i < days; i++){
17089             intDay = i - startingPos + 1;
17090             textEls[i].innerHTML = (intDay);
17091             d.setDate(d.getDate()+1);
17092             
17093             cells[i].className = ''; // "x-date-active";
17094             setCellClass(this, cells[i]);
17095         }
17096         var extraDays = 0;
17097         
17098         for(; i < 42; i++) {
17099             textEls[i].innerHTML = (++extraDays);
17100             d.setDate(d.getDate()+1);
17101             
17102             cells[i].className = "fc-future fc-other-month";
17103             setCellClass(this, cells[i]);
17104         }
17105         
17106         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17107         
17108         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17109         
17110         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17111         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17112         
17113         if(totalRows != 6){
17114             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17115             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17116         }
17117         
17118         this.fireEvent('monthchange', this, date);
17119         
17120         
17121         /*
17122         if(!this.internalRender){
17123             var main = this.el.dom.firstChild;
17124             var w = main.offsetWidth;
17125             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17126             Roo.fly(main).setWidth(w);
17127             this.internalRender = true;
17128             // opera does not respect the auto grow header center column
17129             // then, after it gets a width opera refuses to recalculate
17130             // without a second pass
17131             if(Roo.isOpera && !this.secondPass){
17132                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17133                 this.secondPass = true;
17134                 this.update.defer(10, this, [date]);
17135             }
17136         }
17137         */
17138         
17139     },
17140     
17141     findCell : function(dt) {
17142         dt = dt.clearTime().getTime();
17143         var ret = false;
17144         this.cells.each(function(c){
17145             //Roo.log("check " +c.dateValue + '?=' + dt);
17146             if(c.dateValue == dt){
17147                 ret = c;
17148                 return false;
17149             }
17150             return true;
17151         });
17152         
17153         return ret;
17154     },
17155     
17156     findCells : function(ev) {
17157         var s = ev.start.clone().clearTime().getTime();
17158        // Roo.log(s);
17159         var e= ev.end.clone().clearTime().getTime();
17160        // Roo.log(e);
17161         var ret = [];
17162         this.cells.each(function(c){
17163              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17164             
17165             if(c.dateValue > e){
17166                 return ;
17167             }
17168             if(c.dateValue < s){
17169                 return ;
17170             }
17171             ret.push(c);
17172         });
17173         
17174         return ret;    
17175     },
17176     
17177 //    findBestRow: function(cells)
17178 //    {
17179 //        var ret = 0;
17180 //        
17181 //        for (var i =0 ; i < cells.length;i++) {
17182 //            ret  = Math.max(cells[i].rows || 0,ret);
17183 //        }
17184 //        return ret;
17185 //        
17186 //    },
17187     
17188     
17189     addItem : function(ev)
17190     {
17191         // look for vertical location slot in
17192         var cells = this.findCells(ev);
17193         
17194 //        ev.row = this.findBestRow(cells);
17195         
17196         // work out the location.
17197         
17198         var crow = false;
17199         var rows = [];
17200         for(var i =0; i < cells.length; i++) {
17201             
17202             cells[i].row = cells[0].row;
17203             
17204             if(i == 0){
17205                 cells[i].row = cells[i].row + 1;
17206             }
17207             
17208             if (!crow) {
17209                 crow = {
17210                     start : cells[i],
17211                     end :  cells[i]
17212                 };
17213                 continue;
17214             }
17215             if (crow.start.getY() == cells[i].getY()) {
17216                 // on same row.
17217                 crow.end = cells[i];
17218                 continue;
17219             }
17220             // different row.
17221             rows.push(crow);
17222             crow = {
17223                 start: cells[i],
17224                 end : cells[i]
17225             };
17226             
17227         }
17228         
17229         rows.push(crow);
17230         ev.els = [];
17231         ev.rows = rows;
17232         ev.cells = cells;
17233         
17234         cells[0].events.push(ev);
17235         
17236         this.calevents.push(ev);
17237     },
17238     
17239     clearEvents: function() {
17240         
17241         if(!this.calevents){
17242             return;
17243         }
17244         
17245         Roo.each(this.cells.elements, function(c){
17246             c.row = 0;
17247             c.events = [];
17248             c.more = [];
17249         });
17250         
17251         Roo.each(this.calevents, function(e) {
17252             Roo.each(e.els, function(el) {
17253                 el.un('mouseenter' ,this.onEventEnter, this);
17254                 el.un('mouseleave' ,this.onEventLeave, this);
17255                 el.remove();
17256             },this);
17257         },this);
17258         
17259         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17260             e.remove();
17261         });
17262         
17263     },
17264     
17265     renderEvents: function()
17266     {   
17267         var _this = this;
17268         
17269         this.cells.each(function(c) {
17270             
17271             if(c.row < 5){
17272                 return;
17273             }
17274             
17275             var ev = c.events;
17276             
17277             var r = 4;
17278             if(c.row != c.events.length){
17279                 r = 4 - (4 - (c.row - c.events.length));
17280             }
17281             
17282             c.events = ev.slice(0, r);
17283             c.more = ev.slice(r);
17284             
17285             if(c.more.length && c.more.length == 1){
17286                 c.events.push(c.more.pop());
17287             }
17288             
17289             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17290             
17291         });
17292             
17293         this.cells.each(function(c) {
17294             
17295             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17296             
17297             
17298             for (var e = 0; e < c.events.length; e++){
17299                 var ev = c.events[e];
17300                 var rows = ev.rows;
17301                 
17302                 for(var i = 0; i < rows.length; i++) {
17303                 
17304                     // how many rows should it span..
17305
17306                     var  cfg = {
17307                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17308                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17309
17310                         unselectable : "on",
17311                         cn : [
17312                             {
17313                                 cls: 'fc-event-inner',
17314                                 cn : [
17315     //                                {
17316     //                                  tag:'span',
17317     //                                  cls: 'fc-event-time',
17318     //                                  html : cells.length > 1 ? '' : ev.time
17319     //                                },
17320                                     {
17321                                       tag:'span',
17322                                       cls: 'fc-event-title',
17323                                       html : String.format('{0}', ev.title)
17324                                     }
17325
17326
17327                                 ]
17328                             },
17329                             {
17330                                 cls: 'ui-resizable-handle ui-resizable-e',
17331                                 html : '&nbsp;&nbsp;&nbsp'
17332                             }
17333
17334                         ]
17335                     };
17336
17337                     if (i == 0) {
17338                         cfg.cls += ' fc-event-start';
17339                     }
17340                     if ((i+1) == rows.length) {
17341                         cfg.cls += ' fc-event-end';
17342                     }
17343
17344                     var ctr = _this.el.select('.fc-event-container',true).first();
17345                     var cg = ctr.createChild(cfg);
17346
17347                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17348                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17349
17350                     var r = (c.more.length) ? 1 : 0;
17351                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17352                     cg.setWidth(ebox.right - sbox.x -2);
17353
17354                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17355                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17356                     cg.on('click', _this.onEventClick, _this, ev);
17357
17358                     ev.els.push(cg);
17359                     
17360                 }
17361                 
17362             }
17363             
17364             
17365             if(c.more.length){
17366                 var  cfg = {
17367                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17368                     style : 'position: absolute',
17369                     unselectable : "on",
17370                     cn : [
17371                         {
17372                             cls: 'fc-event-inner',
17373                             cn : [
17374                                 {
17375                                   tag:'span',
17376                                   cls: 'fc-event-title',
17377                                   html : 'More'
17378                                 }
17379
17380
17381                             ]
17382                         },
17383                         {
17384                             cls: 'ui-resizable-handle ui-resizable-e',
17385                             html : '&nbsp;&nbsp;&nbsp'
17386                         }
17387
17388                     ]
17389                 };
17390
17391                 var ctr = _this.el.select('.fc-event-container',true).first();
17392                 var cg = ctr.createChild(cfg);
17393
17394                 var sbox = c.select('.fc-day-content',true).first().getBox();
17395                 var ebox = c.select('.fc-day-content',true).first().getBox();
17396                 //Roo.log(cg);
17397                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17398                 cg.setWidth(ebox.right - sbox.x -2);
17399
17400                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17401                 
17402             }
17403             
17404         });
17405         
17406         
17407         
17408     },
17409     
17410     onEventEnter: function (e, el,event,d) {
17411         this.fireEvent('evententer', this, el, event);
17412     },
17413     
17414     onEventLeave: function (e, el,event,d) {
17415         this.fireEvent('eventleave', this, el, event);
17416     },
17417     
17418     onEventClick: function (e, el,event,d) {
17419         this.fireEvent('eventclick', this, el, event);
17420     },
17421     
17422     onMonthChange: function () {
17423         this.store.load();
17424     },
17425     
17426     onMoreEventClick: function(e, el, more)
17427     {
17428         var _this = this;
17429         
17430         this.calpopover.placement = 'right';
17431         this.calpopover.setTitle('More');
17432         
17433         this.calpopover.setContent('');
17434         
17435         var ctr = this.calpopover.el.select('.popover-content', true).first();
17436         
17437         Roo.each(more, function(m){
17438             var cfg = {
17439                 cls : 'fc-event-hori fc-event-draggable',
17440                 html : m.title
17441             };
17442             var cg = ctr.createChild(cfg);
17443             
17444             cg.on('click', _this.onEventClick, _this, m);
17445         });
17446         
17447         this.calpopover.show(el);
17448         
17449         
17450     },
17451     
17452     onLoad: function () 
17453     {   
17454         this.calevents = [];
17455         var cal = this;
17456         
17457         if(this.store.getCount() > 0){
17458             this.store.data.each(function(d){
17459                cal.addItem({
17460                     id : d.data.id,
17461                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17462                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17463                     time : d.data.start_time,
17464                     title : d.data.title,
17465                     description : d.data.description,
17466                     venue : d.data.venue
17467                 });
17468             });
17469         }
17470         
17471         this.renderEvents();
17472         
17473         if(this.calevents.length && this.loadMask){
17474             this.maskEl.hide();
17475         }
17476     },
17477     
17478     onBeforeLoad: function()
17479     {
17480         this.clearEvents();
17481         if(this.loadMask){
17482             this.maskEl.show();
17483         }
17484     }
17485 });
17486
17487  
17488  /*
17489  * - LGPL
17490  *
17491  * element
17492  * 
17493  */
17494
17495 /**
17496  * @class Roo.bootstrap.Popover
17497  * @extends Roo.bootstrap.Component
17498  * Bootstrap Popover class
17499  * @cfg {String} html contents of the popover   (or false to use children..)
17500  * @cfg {String} title of popover (or false to hide)
17501  * @cfg {String} placement how it is placed
17502  * @cfg {String} trigger click || hover (or false to trigger manually)
17503  * @cfg {String} over what (parent or false to trigger manually.)
17504  * @cfg {Number} delay - delay before showing
17505  
17506  * @constructor
17507  * Create a new Popover
17508  * @param {Object} config The config object
17509  */
17510
17511 Roo.bootstrap.Popover = function(config){
17512     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17513     
17514     this.addEvents({
17515         // raw events
17516          /**
17517          * @event show
17518          * After the popover show
17519          * 
17520          * @param {Roo.bootstrap.Popover} this
17521          */
17522         "show" : true,
17523         /**
17524          * @event hide
17525          * After the popover hide
17526          * 
17527          * @param {Roo.bootstrap.Popover} this
17528          */
17529         "hide" : true
17530     });
17531 };
17532
17533 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17534     
17535     title: 'Fill in a title',
17536     html: false,
17537     
17538     placement : 'right',
17539     trigger : 'hover', // hover
17540     
17541     delay : 0,
17542     
17543     over: 'parent',
17544     
17545     can_build_overlaid : false,
17546     
17547     getChildContainer : function()
17548     {
17549         return this.el.select('.popover-content',true).first();
17550     },
17551     
17552     getAutoCreate : function(){
17553          
17554         var cfg = {
17555            cls : 'popover roo-dynamic',
17556            style: 'display:block',
17557            cn : [
17558                 {
17559                     cls : 'arrow'
17560                 },
17561                 {
17562                     cls : 'popover-inner',
17563                     cn : [
17564                         {
17565                             tag: 'h3',
17566                             cls: 'popover-title',
17567                             html : this.title
17568                         },
17569                         {
17570                             cls : 'popover-content',
17571                             html : this.html
17572                         }
17573                     ]
17574                     
17575                 }
17576            ]
17577         };
17578         
17579         return cfg;
17580     },
17581     setTitle: function(str)
17582     {
17583         this.title = str;
17584         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17585     },
17586     setContent: function(str)
17587     {
17588         this.html = str;
17589         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17590     },
17591     // as it get's added to the bottom of the page.
17592     onRender : function(ct, position)
17593     {
17594         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17595         if(!this.el){
17596             var cfg = Roo.apply({},  this.getAutoCreate());
17597             cfg.id = Roo.id();
17598             
17599             if (this.cls) {
17600                 cfg.cls += ' ' + this.cls;
17601             }
17602             if (this.style) {
17603                 cfg.style = this.style;
17604             }
17605             //Roo.log("adding to ");
17606             this.el = Roo.get(document.body).createChild(cfg, position);
17607 //            Roo.log(this.el);
17608         }
17609         this.initEvents();
17610     },
17611     
17612     initEvents : function()
17613     {
17614         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17615         this.el.enableDisplayMode('block');
17616         this.el.hide();
17617         if (this.over === false) {
17618             return; 
17619         }
17620         if (this.triggers === false) {
17621             return;
17622         }
17623         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17624         var triggers = this.trigger ? this.trigger.split(' ') : [];
17625         Roo.each(triggers, function(trigger) {
17626         
17627             if (trigger == 'click') {
17628                 on_el.on('click', this.toggle, this);
17629             } else if (trigger != 'manual') {
17630                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17631                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17632       
17633                 on_el.on(eventIn  ,this.enter, this);
17634                 on_el.on(eventOut, this.leave, this);
17635             }
17636         }, this);
17637         
17638     },
17639     
17640     
17641     // private
17642     timeout : null,
17643     hoverState : null,
17644     
17645     toggle : function () {
17646         this.hoverState == 'in' ? this.leave() : this.enter();
17647     },
17648     
17649     enter : function () {
17650         
17651         clearTimeout(this.timeout);
17652     
17653         this.hoverState = 'in';
17654     
17655         if (!this.delay || !this.delay.show) {
17656             this.show();
17657             return;
17658         }
17659         var _t = this;
17660         this.timeout = setTimeout(function () {
17661             if (_t.hoverState == 'in') {
17662                 _t.show();
17663             }
17664         }, this.delay.show)
17665     },
17666     
17667     leave : function() {
17668         clearTimeout(this.timeout);
17669     
17670         this.hoverState = 'out';
17671     
17672         if (!this.delay || !this.delay.hide) {
17673             this.hide();
17674             return;
17675         }
17676         var _t = this;
17677         this.timeout = setTimeout(function () {
17678             if (_t.hoverState == 'out') {
17679                 _t.hide();
17680             }
17681         }, this.delay.hide)
17682     },
17683     
17684     show : function (on_el)
17685     {
17686         if (!on_el) {
17687             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17688         }
17689         
17690         // set content.
17691         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17692         if (this.html !== false) {
17693             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17694         }
17695         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17696         if (!this.title.length) {
17697             this.el.select('.popover-title',true).hide();
17698         }
17699         
17700         var placement = typeof this.placement == 'function' ?
17701             this.placement.call(this, this.el, on_el) :
17702             this.placement;
17703             
17704         var autoToken = /\s?auto?\s?/i;
17705         var autoPlace = autoToken.test(placement);
17706         if (autoPlace) {
17707             placement = placement.replace(autoToken, '') || 'top';
17708         }
17709         
17710         //this.el.detach()
17711         //this.el.setXY([0,0]);
17712         this.el.show();
17713         this.el.dom.style.display='block';
17714         this.el.addClass(placement);
17715         
17716         //this.el.appendTo(on_el);
17717         
17718         var p = this.getPosition();
17719         var box = this.el.getBox();
17720         
17721         if (autoPlace) {
17722             // fixme..
17723         }
17724         var align = Roo.bootstrap.Popover.alignment[placement];
17725         
17726 //        Roo.log(align);
17727         this.el.alignTo(on_el, align[0],align[1]);
17728         //var arrow = this.el.select('.arrow',true).first();
17729         //arrow.set(align[2], 
17730         
17731         this.el.addClass('in');
17732         
17733         
17734         if (this.el.hasClass('fade')) {
17735             // fade it?
17736         }
17737         
17738         this.hoverState = 'in';
17739         
17740         this.fireEvent('show', this);
17741         
17742     },
17743     hide : function()
17744     {
17745         this.el.setXY([0,0]);
17746         this.el.removeClass('in');
17747         this.el.hide();
17748         this.hoverState = null;
17749         
17750         this.fireEvent('hide', this);
17751     }
17752     
17753 });
17754
17755 Roo.bootstrap.Popover.alignment = {
17756     'left' : ['r-l', [-10,0], 'right'],
17757     'right' : ['l-r', [10,0], 'left'],
17758     'bottom' : ['t-b', [0,10], 'top'],
17759     'top' : [ 'b-t', [0,-10], 'bottom']
17760 };
17761
17762  /*
17763  * - LGPL
17764  *
17765  * Progress
17766  * 
17767  */
17768
17769 /**
17770  * @class Roo.bootstrap.Progress
17771  * @extends Roo.bootstrap.Component
17772  * Bootstrap Progress class
17773  * @cfg {Boolean} striped striped of the progress bar
17774  * @cfg {Boolean} active animated of the progress bar
17775  * 
17776  * 
17777  * @constructor
17778  * Create a new Progress
17779  * @param {Object} config The config object
17780  */
17781
17782 Roo.bootstrap.Progress = function(config){
17783     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17784 };
17785
17786 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17787     
17788     striped : false,
17789     active: false,
17790     
17791     getAutoCreate : function(){
17792         var cfg = {
17793             tag: 'div',
17794             cls: 'progress'
17795         };
17796         
17797         
17798         if(this.striped){
17799             cfg.cls += ' progress-striped';
17800         }
17801       
17802         if(this.active){
17803             cfg.cls += ' active';
17804         }
17805         
17806         
17807         return cfg;
17808     }
17809    
17810 });
17811
17812  
17813
17814  /*
17815  * - LGPL
17816  *
17817  * ProgressBar
17818  * 
17819  */
17820
17821 /**
17822  * @class Roo.bootstrap.ProgressBar
17823  * @extends Roo.bootstrap.Component
17824  * Bootstrap ProgressBar class
17825  * @cfg {Number} aria_valuenow aria-value now
17826  * @cfg {Number} aria_valuemin aria-value min
17827  * @cfg {Number} aria_valuemax aria-value max
17828  * @cfg {String} label label for the progress bar
17829  * @cfg {String} panel (success | info | warning | danger )
17830  * @cfg {String} role role of the progress bar
17831  * @cfg {String} sr_only text
17832  * 
17833  * 
17834  * @constructor
17835  * Create a new ProgressBar
17836  * @param {Object} config The config object
17837  */
17838
17839 Roo.bootstrap.ProgressBar = function(config){
17840     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17841 };
17842
17843 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17844     
17845     aria_valuenow : 0,
17846     aria_valuemin : 0,
17847     aria_valuemax : 100,
17848     label : false,
17849     panel : false,
17850     role : false,
17851     sr_only: false,
17852     
17853     getAutoCreate : function()
17854     {
17855         
17856         var cfg = {
17857             tag: 'div',
17858             cls: 'progress-bar',
17859             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17860         };
17861         
17862         if(this.sr_only){
17863             cfg.cn = {
17864                 tag: 'span',
17865                 cls: 'sr-only',
17866                 html: this.sr_only
17867             }
17868         }
17869         
17870         if(this.role){
17871             cfg.role = this.role;
17872         }
17873         
17874         if(this.aria_valuenow){
17875             cfg['aria-valuenow'] = this.aria_valuenow;
17876         }
17877         
17878         if(this.aria_valuemin){
17879             cfg['aria-valuemin'] = this.aria_valuemin;
17880         }
17881         
17882         if(this.aria_valuemax){
17883             cfg['aria-valuemax'] = this.aria_valuemax;
17884         }
17885         
17886         if(this.label && !this.sr_only){
17887             cfg.html = this.label;
17888         }
17889         
17890         if(this.panel){
17891             cfg.cls += ' progress-bar-' + this.panel;
17892         }
17893         
17894         return cfg;
17895     },
17896     
17897     update : function(aria_valuenow)
17898     {
17899         this.aria_valuenow = aria_valuenow;
17900         
17901         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17902     }
17903    
17904 });
17905
17906  
17907
17908  /*
17909  * - LGPL
17910  *
17911  * column
17912  * 
17913  */
17914
17915 /**
17916  * @class Roo.bootstrap.TabGroup
17917  * @extends Roo.bootstrap.Column
17918  * Bootstrap Column class
17919  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17920  * @cfg {Boolean} carousel true to make the group behave like a carousel
17921  * @cfg {Boolean} bullets show bullets for the panels
17922  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17923  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17924  * @cfg {Boolean} showarrow (true|false) show arrow default true
17925  * 
17926  * @constructor
17927  * Create a new TabGroup
17928  * @param {Object} config The config object
17929  */
17930
17931 Roo.bootstrap.TabGroup = function(config){
17932     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17933     if (!this.navId) {
17934         this.navId = Roo.id();
17935     }
17936     this.tabs = [];
17937     Roo.bootstrap.TabGroup.register(this);
17938     
17939 };
17940
17941 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17942     
17943     carousel : false,
17944     transition : false,
17945     bullets : 0,
17946     timer : 0,
17947     autoslide : false,
17948     slideFn : false,
17949     slideOnTouch : false,
17950     showarrow : true,
17951     
17952     getAutoCreate : function()
17953     {
17954         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17955         
17956         cfg.cls += ' tab-content';
17957         
17958         if (this.carousel) {
17959             cfg.cls += ' carousel slide';
17960             
17961             cfg.cn = [{
17962                cls : 'carousel-inner',
17963                cn : []
17964             }];
17965         
17966             if(this.bullets  && !Roo.isTouch){
17967                 
17968                 var bullets = {
17969                     cls : 'carousel-bullets',
17970                     cn : []
17971                 };
17972                
17973                 if(this.bullets_cls){
17974                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17975                 }
17976                 
17977                 bullets.cn.push({
17978                     cls : 'clear'
17979                 });
17980                 
17981                 cfg.cn[0].cn.push(bullets);
17982             }
17983             
17984             if(this.showarrow){
17985                 cfg.cn[0].cn.push({
17986                     tag : 'div',
17987                     class : 'carousel-arrow',
17988                     cn : [
17989                         {
17990                             tag : 'div',
17991                             class : 'carousel-prev',
17992                             cn : [
17993                                 {
17994                                     tag : 'i',
17995                                     class : 'fa fa-chevron-left'
17996                                 }
17997                             ]
17998                         },
17999                         {
18000                             tag : 'div',
18001                             class : 'carousel-next',
18002                             cn : [
18003                                 {
18004                                     tag : 'i',
18005                                     class : 'fa fa-chevron-right'
18006                                 }
18007                             ]
18008                         }
18009                     ]
18010                 });
18011             }
18012             
18013         }
18014         
18015         return cfg;
18016     },
18017     
18018     initEvents:  function()
18019     {
18020 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18021 //            this.el.on("touchstart", this.onTouchStart, this);
18022 //        }
18023         
18024         if(this.autoslide){
18025             var _this = this;
18026             
18027             this.slideFn = window.setInterval(function() {
18028                 _this.showPanelNext();
18029             }, this.timer);
18030         }
18031         
18032         if(this.showarrow){
18033             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18034             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18035         }
18036         
18037         
18038     },
18039     
18040 //    onTouchStart : function(e, el, o)
18041 //    {
18042 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18043 //            return;
18044 //        }
18045 //        
18046 //        this.showPanelNext();
18047 //    },
18048     
18049     
18050     getChildContainer : function()
18051     {
18052         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18053     },
18054     
18055     /**
18056     * register a Navigation item
18057     * @param {Roo.bootstrap.NavItem} the navitem to add
18058     */
18059     register : function(item)
18060     {
18061         this.tabs.push( item);
18062         item.navId = this.navId; // not really needed..
18063         this.addBullet();
18064     
18065     },
18066     
18067     getActivePanel : function()
18068     {
18069         var r = false;
18070         Roo.each(this.tabs, function(t) {
18071             if (t.active) {
18072                 r = t;
18073                 return false;
18074             }
18075             return null;
18076         });
18077         return r;
18078         
18079     },
18080     getPanelByName : function(n)
18081     {
18082         var r = false;
18083         Roo.each(this.tabs, function(t) {
18084             if (t.tabId == n) {
18085                 r = t;
18086                 return false;
18087             }
18088             return null;
18089         });
18090         return r;
18091     },
18092     indexOfPanel : function(p)
18093     {
18094         var r = false;
18095         Roo.each(this.tabs, function(t,i) {
18096             if (t.tabId == p.tabId) {
18097                 r = i;
18098                 return false;
18099             }
18100             return null;
18101         });
18102         return r;
18103     },
18104     /**
18105      * show a specific panel
18106      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18107      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18108      */
18109     showPanel : function (pan)
18110     {
18111         if(this.transition || typeof(pan) == 'undefined'){
18112             Roo.log("waiting for the transitionend");
18113             return;
18114         }
18115         
18116         if (typeof(pan) == 'number') {
18117             pan = this.tabs[pan];
18118         }
18119         
18120         if (typeof(pan) == 'string') {
18121             pan = this.getPanelByName(pan);
18122         }
18123         
18124         var cur = this.getActivePanel();
18125         
18126         if(!pan || !cur){
18127             Roo.log('pan or acitve pan is undefined');
18128             return false;
18129         }
18130         
18131         if (pan.tabId == this.getActivePanel().tabId) {
18132             return true;
18133         }
18134         
18135         if (false === cur.fireEvent('beforedeactivate')) {
18136             return false;
18137         }
18138         
18139         if(this.bullets > 0 && !Roo.isTouch){
18140             this.setActiveBullet(this.indexOfPanel(pan));
18141         }
18142         
18143         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18144             
18145             this.transition = true;
18146             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18147             var lr = dir == 'next' ? 'left' : 'right';
18148             pan.el.addClass(dir); // or prev
18149             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18150             cur.el.addClass(lr); // or right
18151             pan.el.addClass(lr);
18152             
18153             var _this = this;
18154             cur.el.on('transitionend', function() {
18155                 Roo.log("trans end?");
18156                 
18157                 pan.el.removeClass([lr,dir]);
18158                 pan.setActive(true);
18159                 
18160                 cur.el.removeClass([lr]);
18161                 cur.setActive(false);
18162                 
18163                 _this.transition = false;
18164                 
18165             }, this, { single:  true } );
18166             
18167             return true;
18168         }
18169         
18170         cur.setActive(false);
18171         pan.setActive(true);
18172         
18173         return true;
18174         
18175     },
18176     showPanelNext : function()
18177     {
18178         var i = this.indexOfPanel(this.getActivePanel());
18179         
18180         if (i >= this.tabs.length - 1 && !this.autoslide) {
18181             return;
18182         }
18183         
18184         if (i >= this.tabs.length - 1 && this.autoslide) {
18185             i = -1;
18186         }
18187         
18188         this.showPanel(this.tabs[i+1]);
18189     },
18190     
18191     showPanelPrev : function()
18192     {
18193         var i = this.indexOfPanel(this.getActivePanel());
18194         
18195         if (i  < 1 && !this.autoslide) {
18196             return;
18197         }
18198         
18199         if (i < 1 && this.autoslide) {
18200             i = this.tabs.length;
18201         }
18202         
18203         this.showPanel(this.tabs[i-1]);
18204     },
18205     
18206     
18207     addBullet: function()
18208     {
18209         if(!this.bullets || Roo.isTouch){
18210             return;
18211         }
18212         var ctr = this.el.select('.carousel-bullets',true).first();
18213         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18214         var bullet = ctr.createChild({
18215             cls : 'bullet bullet-' + i
18216         },ctr.dom.lastChild);
18217         
18218         
18219         var _this = this;
18220         
18221         bullet.on('click', (function(e, el, o, ii, t){
18222
18223             e.preventDefault();
18224
18225             this.showPanel(ii);
18226
18227             if(this.autoslide && this.slideFn){
18228                 clearInterval(this.slideFn);
18229                 this.slideFn = window.setInterval(function() {
18230                     _this.showPanelNext();
18231                 }, this.timer);
18232             }
18233
18234         }).createDelegate(this, [i, bullet], true));
18235                 
18236         
18237     },
18238      
18239     setActiveBullet : function(i)
18240     {
18241         if(Roo.isTouch){
18242             return;
18243         }
18244         
18245         Roo.each(this.el.select('.bullet', true).elements, function(el){
18246             el.removeClass('selected');
18247         });
18248
18249         var bullet = this.el.select('.bullet-' + i, true).first();
18250         
18251         if(!bullet){
18252             return;
18253         }
18254         
18255         bullet.addClass('selected');
18256     }
18257     
18258     
18259   
18260 });
18261
18262  
18263
18264  
18265  
18266 Roo.apply(Roo.bootstrap.TabGroup, {
18267     
18268     groups: {},
18269      /**
18270     * register a Navigation Group
18271     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18272     */
18273     register : function(navgrp)
18274     {
18275         this.groups[navgrp.navId] = navgrp;
18276         
18277     },
18278     /**
18279     * fetch a Navigation Group based on the navigation ID
18280     * if one does not exist , it will get created.
18281     * @param {string} the navgroup to add
18282     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18283     */
18284     get: function(navId) {
18285         if (typeof(this.groups[navId]) == 'undefined') {
18286             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18287         }
18288         return this.groups[navId] ;
18289     }
18290     
18291     
18292     
18293 });
18294
18295  /*
18296  * - LGPL
18297  *
18298  * TabPanel
18299  * 
18300  */
18301
18302 /**
18303  * @class Roo.bootstrap.TabPanel
18304  * @extends Roo.bootstrap.Component
18305  * Bootstrap TabPanel class
18306  * @cfg {Boolean} active panel active
18307  * @cfg {String} html panel content
18308  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18309  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18310  * @cfg {String} href click to link..
18311  * 
18312  * 
18313  * @constructor
18314  * Create a new TabPanel
18315  * @param {Object} config The config object
18316  */
18317
18318 Roo.bootstrap.TabPanel = function(config){
18319     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18320     this.addEvents({
18321         /**
18322              * @event changed
18323              * Fires when the active status changes
18324              * @param {Roo.bootstrap.TabPanel} this
18325              * @param {Boolean} state the new state
18326             
18327          */
18328         'changed': true,
18329         /**
18330              * @event beforedeactivate
18331              * Fires before a tab is de-activated - can be used to do validation on a form.
18332              * @param {Roo.bootstrap.TabPanel} this
18333              * @return {Boolean} false if there is an error
18334             
18335          */
18336         'beforedeactivate': true
18337      });
18338     
18339     this.tabId = this.tabId || Roo.id();
18340   
18341 };
18342
18343 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18344     
18345     active: false,
18346     html: false,
18347     tabId: false,
18348     navId : false,
18349     href : '',
18350     
18351     getAutoCreate : function(){
18352         var cfg = {
18353             tag: 'div',
18354             // item is needed for carousel - not sure if it has any effect otherwise
18355             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18356             html: this.html || ''
18357         };
18358         
18359         if(this.active){
18360             cfg.cls += ' active';
18361         }
18362         
18363         if(this.tabId){
18364             cfg.tabId = this.tabId;
18365         }
18366         
18367         
18368         return cfg;
18369     },
18370     
18371     initEvents:  function()
18372     {
18373         var p = this.parent();
18374         
18375         this.navId = this.navId || p.navId;
18376         
18377         if (typeof(this.navId) != 'undefined') {
18378             // not really needed.. but just in case.. parent should be a NavGroup.
18379             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18380             
18381             tg.register(this);
18382             
18383             var i = tg.tabs.length - 1;
18384             
18385             if(this.active && tg.bullets > 0 && i < tg.bullets){
18386                 tg.setActiveBullet(i);
18387             }
18388         }
18389         
18390         this.el.on('click', this.onClick, this);
18391         
18392         if(Roo.isTouch){
18393             this.el.on("touchstart", this.onTouchStart, this);
18394             this.el.on("touchmove", this.onTouchMove, this);
18395             this.el.on("touchend", this.onTouchEnd, this);
18396         }
18397         
18398     },
18399     
18400     onRender : function(ct, position)
18401     {
18402         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18403     },
18404     
18405     setActive : function(state)
18406     {
18407         Roo.log("panel - set active " + this.tabId + "=" + state);
18408         
18409         this.active = state;
18410         if (!state) {
18411             this.el.removeClass('active');
18412             
18413         } else  if (!this.el.hasClass('active')) {
18414             this.el.addClass('active');
18415         }
18416         
18417         this.fireEvent('changed', this, state);
18418     },
18419     
18420     onClick : function(e)
18421     {
18422         e.preventDefault();
18423         
18424         if(!this.href.length){
18425             return;
18426         }
18427         
18428         window.location.href = this.href;
18429     },
18430     
18431     startX : 0,
18432     startY : 0,
18433     endX : 0,
18434     endY : 0,
18435     swiping : false,
18436     
18437     onTouchStart : function(e)
18438     {
18439         this.swiping = false;
18440         
18441         this.startX = e.browserEvent.touches[0].clientX;
18442         this.startY = e.browserEvent.touches[0].clientY;
18443     },
18444     
18445     onTouchMove : function(e)
18446     {
18447         this.swiping = true;
18448         
18449         this.endX = e.browserEvent.touches[0].clientX;
18450         this.endY = e.browserEvent.touches[0].clientY;
18451     },
18452     
18453     onTouchEnd : function(e)
18454     {
18455         if(!this.swiping){
18456             this.onClick(e);
18457             return;
18458         }
18459         
18460         var tabGroup = this.parent();
18461         
18462         if(this.endX > this.startX){ // swiping right
18463             tabGroup.showPanelPrev();
18464             return;
18465         }
18466         
18467         if(this.startX > this.endX){ // swiping left
18468             tabGroup.showPanelNext();
18469             return;
18470         }
18471     }
18472     
18473     
18474 });
18475  
18476
18477  
18478
18479  /*
18480  * - LGPL
18481  *
18482  * DateField
18483  * 
18484  */
18485
18486 /**
18487  * @class Roo.bootstrap.DateField
18488  * @extends Roo.bootstrap.Input
18489  * Bootstrap DateField class
18490  * @cfg {Number} weekStart default 0
18491  * @cfg {String} viewMode default empty, (months|years)
18492  * @cfg {String} minViewMode default empty, (months|years)
18493  * @cfg {Number} startDate default -Infinity
18494  * @cfg {Number} endDate default Infinity
18495  * @cfg {Boolean} todayHighlight default false
18496  * @cfg {Boolean} todayBtn default false
18497  * @cfg {Boolean} calendarWeeks default false
18498  * @cfg {Object} daysOfWeekDisabled default empty
18499  * @cfg {Boolean} singleMode default false (true | false)
18500  * 
18501  * @cfg {Boolean} keyboardNavigation default true
18502  * @cfg {String} language default en
18503  * 
18504  * @constructor
18505  * Create a new DateField
18506  * @param {Object} config The config object
18507  */
18508
18509 Roo.bootstrap.DateField = function(config){
18510     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18511      this.addEvents({
18512             /**
18513              * @event show
18514              * Fires when this field show.
18515              * @param {Roo.bootstrap.DateField} this
18516              * @param {Mixed} date The date value
18517              */
18518             show : true,
18519             /**
18520              * @event show
18521              * Fires when this field hide.
18522              * @param {Roo.bootstrap.DateField} this
18523              * @param {Mixed} date The date value
18524              */
18525             hide : true,
18526             /**
18527              * @event select
18528              * Fires when select a date.
18529              * @param {Roo.bootstrap.DateField} this
18530              * @param {Mixed} date The date value
18531              */
18532             select : true,
18533             /**
18534              * @event beforeselect
18535              * Fires when before select a date.
18536              * @param {Roo.bootstrap.DateField} this
18537              * @param {Mixed} date The date value
18538              */
18539             beforeselect : true
18540         });
18541 };
18542
18543 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18544     
18545     /**
18546      * @cfg {String} format
18547      * The default date format string which can be overriden for localization support.  The format must be
18548      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18549      */
18550     format : "m/d/y",
18551     /**
18552      * @cfg {String} altFormats
18553      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18554      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18555      */
18556     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18557     
18558     weekStart : 0,
18559     
18560     viewMode : '',
18561     
18562     minViewMode : '',
18563     
18564     todayHighlight : false,
18565     
18566     todayBtn: false,
18567     
18568     language: 'en',
18569     
18570     keyboardNavigation: true,
18571     
18572     calendarWeeks: false,
18573     
18574     startDate: -Infinity,
18575     
18576     endDate: Infinity,
18577     
18578     daysOfWeekDisabled: [],
18579     
18580     _events: [],
18581     
18582     singleMode : false,
18583     
18584     UTCDate: function()
18585     {
18586         return new Date(Date.UTC.apply(Date, arguments));
18587     },
18588     
18589     UTCToday: function()
18590     {
18591         var today = new Date();
18592         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18593     },
18594     
18595     getDate: function() {
18596             var d = this.getUTCDate();
18597             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18598     },
18599     
18600     getUTCDate: function() {
18601             return this.date;
18602     },
18603     
18604     setDate: function(d) {
18605             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18606     },
18607     
18608     setUTCDate: function(d) {
18609             this.date = d;
18610             this.setValue(this.formatDate(this.date));
18611     },
18612         
18613     onRender: function(ct, position)
18614     {
18615         
18616         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18617         
18618         this.language = this.language || 'en';
18619         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18620         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18621         
18622         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18623         this.format = this.format || 'm/d/y';
18624         this.isInline = false;
18625         this.isInput = true;
18626         this.component = this.el.select('.add-on', true).first() || false;
18627         this.component = (this.component && this.component.length === 0) ? false : this.component;
18628         this.hasInput = this.component && this.inputEl().length;
18629         
18630         if (typeof(this.minViewMode === 'string')) {
18631             switch (this.minViewMode) {
18632                 case 'months':
18633                     this.minViewMode = 1;
18634                     break;
18635                 case 'years':
18636                     this.minViewMode = 2;
18637                     break;
18638                 default:
18639                     this.minViewMode = 0;
18640                     break;
18641             }
18642         }
18643         
18644         if (typeof(this.viewMode === 'string')) {
18645             switch (this.viewMode) {
18646                 case 'months':
18647                     this.viewMode = 1;
18648                     break;
18649                 case 'years':
18650                     this.viewMode = 2;
18651                     break;
18652                 default:
18653                     this.viewMode = 0;
18654                     break;
18655             }
18656         }
18657                 
18658         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18659         
18660 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18661         
18662         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18663         
18664         this.picker().on('mousedown', this.onMousedown, this);
18665         this.picker().on('click', this.onClick, this);
18666         
18667         this.picker().addClass('datepicker-dropdown');
18668         
18669         this.startViewMode = this.viewMode;
18670         
18671         if(this.singleMode){
18672             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18673                 v.setVisibilityMode(Roo.Element.DISPLAY);
18674                 v.hide();
18675             });
18676             
18677             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18678                 v.setStyle('width', '189px');
18679             });
18680         }
18681         
18682         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18683             if(!this.calendarWeeks){
18684                 v.remove();
18685                 return;
18686             }
18687             
18688             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18689             v.attr('colspan', function(i, val){
18690                 return parseInt(val) + 1;
18691             });
18692         });
18693                         
18694         
18695         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18696         
18697         this.setStartDate(this.startDate);
18698         this.setEndDate(this.endDate);
18699         
18700         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18701         
18702         this.fillDow();
18703         this.fillMonths();
18704         this.update();
18705         this.showMode();
18706         
18707         if(this.isInline) {
18708             this.showPopup();
18709         }
18710     },
18711     
18712     picker : function()
18713     {
18714         return this.pickerEl;
18715 //        return this.el.select('.datepicker', true).first();
18716     },
18717     
18718     fillDow: function()
18719     {
18720         var dowCnt = this.weekStart;
18721         
18722         var dow = {
18723             tag: 'tr',
18724             cn: [
18725                 
18726             ]
18727         };
18728         
18729         if(this.calendarWeeks){
18730             dow.cn.push({
18731                 tag: 'th',
18732                 cls: 'cw',
18733                 html: '&nbsp;'
18734             })
18735         }
18736         
18737         while (dowCnt < this.weekStart + 7) {
18738             dow.cn.push({
18739                 tag: 'th',
18740                 cls: 'dow',
18741                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18742             });
18743         }
18744         
18745         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18746     },
18747     
18748     fillMonths: function()
18749     {    
18750         var i = 0;
18751         var months = this.picker().select('>.datepicker-months td', true).first();
18752         
18753         months.dom.innerHTML = '';
18754         
18755         while (i < 12) {
18756             var month = {
18757                 tag: 'span',
18758                 cls: 'month',
18759                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18760             };
18761             
18762             months.createChild(month);
18763         }
18764         
18765     },
18766     
18767     update: function()
18768     {
18769         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;
18770         
18771         if (this.date < this.startDate) {
18772             this.viewDate = new Date(this.startDate);
18773         } else if (this.date > this.endDate) {
18774             this.viewDate = new Date(this.endDate);
18775         } else {
18776             this.viewDate = new Date(this.date);
18777         }
18778         
18779         this.fill();
18780     },
18781     
18782     fill: function() 
18783     {
18784         var d = new Date(this.viewDate),
18785                 year = d.getUTCFullYear(),
18786                 month = d.getUTCMonth(),
18787                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18788                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18789                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18790                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18791                 currentDate = this.date && this.date.valueOf(),
18792                 today = this.UTCToday();
18793         
18794         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18795         
18796 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18797         
18798 //        this.picker.select('>tfoot th.today').
18799 //                                              .text(dates[this.language].today)
18800 //                                              .toggle(this.todayBtn !== false);
18801     
18802         this.updateNavArrows();
18803         this.fillMonths();
18804                                                 
18805         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18806         
18807         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18808          
18809         prevMonth.setUTCDate(day);
18810         
18811         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18812         
18813         var nextMonth = new Date(prevMonth);
18814         
18815         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18816         
18817         nextMonth = nextMonth.valueOf();
18818         
18819         var fillMonths = false;
18820         
18821         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18822         
18823         while(prevMonth.valueOf() <= nextMonth) {
18824             var clsName = '';
18825             
18826             if (prevMonth.getUTCDay() === this.weekStart) {
18827                 if(fillMonths){
18828                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18829                 }
18830                     
18831                 fillMonths = {
18832                     tag: 'tr',
18833                     cn: []
18834                 };
18835                 
18836                 if(this.calendarWeeks){
18837                     // ISO 8601: First week contains first thursday.
18838                     // ISO also states week starts on Monday, but we can be more abstract here.
18839                     var
18840                     // Start of current week: based on weekstart/current date
18841                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18842                     // Thursday of this week
18843                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18844                     // First Thursday of year, year from thursday
18845                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18846                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18847                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18848                     
18849                     fillMonths.cn.push({
18850                         tag: 'td',
18851                         cls: 'cw',
18852                         html: calWeek
18853                     });
18854                 }
18855             }
18856             
18857             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18858                 clsName += ' old';
18859             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18860                 clsName += ' new';
18861             }
18862             if (this.todayHighlight &&
18863                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18864                 prevMonth.getUTCMonth() == today.getMonth() &&
18865                 prevMonth.getUTCDate() == today.getDate()) {
18866                 clsName += ' today';
18867             }
18868             
18869             if (currentDate && prevMonth.valueOf() === currentDate) {
18870                 clsName += ' active';
18871             }
18872             
18873             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18874                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18875                     clsName += ' disabled';
18876             }
18877             
18878             fillMonths.cn.push({
18879                 tag: 'td',
18880                 cls: 'day ' + clsName,
18881                 html: prevMonth.getDate()
18882             });
18883             
18884             prevMonth.setDate(prevMonth.getDate()+1);
18885         }
18886           
18887         var currentYear = this.date && this.date.getUTCFullYear();
18888         var currentMonth = this.date && this.date.getUTCMonth();
18889         
18890         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18891         
18892         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18893             v.removeClass('active');
18894             
18895             if(currentYear === year && k === currentMonth){
18896                 v.addClass('active');
18897             }
18898             
18899             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18900                 v.addClass('disabled');
18901             }
18902             
18903         });
18904         
18905         
18906         year = parseInt(year/10, 10) * 10;
18907         
18908         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18909         
18910         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18911         
18912         year -= 1;
18913         for (var i = -1; i < 11; i++) {
18914             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18915                 tag: 'span',
18916                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18917                 html: year
18918             });
18919             
18920             year += 1;
18921         }
18922     },
18923     
18924     showMode: function(dir) 
18925     {
18926         if (dir) {
18927             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18928         }
18929         
18930         Roo.each(this.picker().select('>div',true).elements, function(v){
18931             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18932             v.hide();
18933         });
18934         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18935     },
18936     
18937     place: function()
18938     {
18939         if(this.isInline) {
18940             return;
18941         }
18942         
18943         this.picker().removeClass(['bottom', 'top']);
18944         
18945         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18946             /*
18947              * place to the top of element!
18948              *
18949              */
18950             
18951             this.picker().addClass('top');
18952             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18953             
18954             return;
18955         }
18956         
18957         this.picker().addClass('bottom');
18958         
18959         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18960     },
18961     
18962     parseDate : function(value)
18963     {
18964         if(!value || value instanceof Date){
18965             return value;
18966         }
18967         var v = Date.parseDate(value, this.format);
18968         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18969             v = Date.parseDate(value, 'Y-m-d');
18970         }
18971         if(!v && this.altFormats){
18972             if(!this.altFormatsArray){
18973                 this.altFormatsArray = this.altFormats.split("|");
18974             }
18975             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18976                 v = Date.parseDate(value, this.altFormatsArray[i]);
18977             }
18978         }
18979         return v;
18980     },
18981     
18982     formatDate : function(date, fmt)
18983     {   
18984         return (!date || !(date instanceof Date)) ?
18985         date : date.dateFormat(fmt || this.format);
18986     },
18987     
18988     onFocus : function()
18989     {
18990         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18991         this.showPopup();
18992     },
18993     
18994     onBlur : function()
18995     {
18996         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18997         
18998         var d = this.inputEl().getValue();
18999         
19000         this.setValue(d);
19001                 
19002         this.hidePopup();
19003     },
19004     
19005     showPopup : function()
19006     {
19007         this.picker().show();
19008         this.update();
19009         this.place();
19010         
19011         this.fireEvent('showpopup', this, this.date);
19012     },
19013     
19014     hidePopup : function()
19015     {
19016         if(this.isInline) {
19017             return;
19018         }
19019         this.picker().hide();
19020         this.viewMode = this.startViewMode;
19021         this.showMode();
19022         
19023         this.fireEvent('hidepopup', this, this.date);
19024         
19025     },
19026     
19027     onMousedown: function(e)
19028     {
19029         e.stopPropagation();
19030         e.preventDefault();
19031     },
19032     
19033     keyup: function(e)
19034     {
19035         Roo.bootstrap.DateField.superclass.keyup.call(this);
19036         this.update();
19037     },
19038
19039     setValue: function(v)
19040     {
19041         if(this.fireEvent('beforeselect', this, v) !== false){
19042             var d = new Date(this.parseDate(v) ).clearTime();
19043         
19044             if(isNaN(d.getTime())){
19045                 this.date = this.viewDate = '';
19046                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19047                 return;
19048             }
19049
19050             v = this.formatDate(d);
19051
19052             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19053
19054             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19055
19056             this.update();
19057
19058             this.fireEvent('select', this, this.date);
19059         }
19060     },
19061     
19062     getValue: function()
19063     {
19064         return this.formatDate(this.date);
19065     },
19066     
19067     fireKey: function(e)
19068     {
19069         if (!this.picker().isVisible()){
19070             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19071                 this.showPopup();
19072             }
19073             return;
19074         }
19075         
19076         var dateChanged = false,
19077         dir, day, month,
19078         newDate, newViewDate;
19079         
19080         switch(e.keyCode){
19081             case 27: // escape
19082                 this.hidePopup();
19083                 e.preventDefault();
19084                 break;
19085             case 37: // left
19086             case 39: // right
19087                 if (!this.keyboardNavigation) {
19088                     break;
19089                 }
19090                 dir = e.keyCode == 37 ? -1 : 1;
19091                 
19092                 if (e.ctrlKey){
19093                     newDate = this.moveYear(this.date, dir);
19094                     newViewDate = this.moveYear(this.viewDate, dir);
19095                 } else if (e.shiftKey){
19096                     newDate = this.moveMonth(this.date, dir);
19097                     newViewDate = this.moveMonth(this.viewDate, dir);
19098                 } else {
19099                     newDate = new Date(this.date);
19100                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19101                     newViewDate = new Date(this.viewDate);
19102                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19103                 }
19104                 if (this.dateWithinRange(newDate)){
19105                     this.date = newDate;
19106                     this.viewDate = newViewDate;
19107                     this.setValue(this.formatDate(this.date));
19108 //                    this.update();
19109                     e.preventDefault();
19110                     dateChanged = true;
19111                 }
19112                 break;
19113             case 38: // up
19114             case 40: // down
19115                 if (!this.keyboardNavigation) {
19116                     break;
19117                 }
19118                 dir = e.keyCode == 38 ? -1 : 1;
19119                 if (e.ctrlKey){
19120                     newDate = this.moveYear(this.date, dir);
19121                     newViewDate = this.moveYear(this.viewDate, dir);
19122                 } else if (e.shiftKey){
19123                     newDate = this.moveMonth(this.date, dir);
19124                     newViewDate = this.moveMonth(this.viewDate, dir);
19125                 } else {
19126                     newDate = new Date(this.date);
19127                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19128                     newViewDate = new Date(this.viewDate);
19129                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19130                 }
19131                 if (this.dateWithinRange(newDate)){
19132                     this.date = newDate;
19133                     this.viewDate = newViewDate;
19134                     this.setValue(this.formatDate(this.date));
19135 //                    this.update();
19136                     e.preventDefault();
19137                     dateChanged = true;
19138                 }
19139                 break;
19140             case 13: // enter
19141                 this.setValue(this.formatDate(this.date));
19142                 this.hidePopup();
19143                 e.preventDefault();
19144                 break;
19145             case 9: // tab
19146                 this.setValue(this.formatDate(this.date));
19147                 this.hidePopup();
19148                 break;
19149             case 16: // shift
19150             case 17: // ctrl
19151             case 18: // alt
19152                 break;
19153             default :
19154                 this.hide();
19155                 
19156         }
19157     },
19158     
19159     
19160     onClick: function(e) 
19161     {
19162         e.stopPropagation();
19163         e.preventDefault();
19164         
19165         var target = e.getTarget();
19166         
19167         if(target.nodeName.toLowerCase() === 'i'){
19168             target = Roo.get(target).dom.parentNode;
19169         }
19170         
19171         var nodeName = target.nodeName;
19172         var className = target.className;
19173         var html = target.innerHTML;
19174         //Roo.log(nodeName);
19175         
19176         switch(nodeName.toLowerCase()) {
19177             case 'th':
19178                 switch(className) {
19179                     case 'switch':
19180                         this.showMode(1);
19181                         break;
19182                     case 'prev':
19183                     case 'next':
19184                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19185                         switch(this.viewMode){
19186                                 case 0:
19187                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19188                                         break;
19189                                 case 1:
19190                                 case 2:
19191                                         this.viewDate = this.moveYear(this.viewDate, dir);
19192                                         break;
19193                         }
19194                         this.fill();
19195                         break;
19196                     case 'today':
19197                         var date = new Date();
19198                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19199 //                        this.fill()
19200                         this.setValue(this.formatDate(this.date));
19201                         
19202                         this.hidePopup();
19203                         break;
19204                 }
19205                 break;
19206             case 'span':
19207                 if (className.indexOf('disabled') < 0) {
19208                     this.viewDate.setUTCDate(1);
19209                     if (className.indexOf('month') > -1) {
19210                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19211                     } else {
19212                         var year = parseInt(html, 10) || 0;
19213                         this.viewDate.setUTCFullYear(year);
19214                         
19215                     }
19216                     
19217                     if(this.singleMode){
19218                         this.setValue(this.formatDate(this.viewDate));
19219                         this.hidePopup();
19220                         return;
19221                     }
19222                     
19223                     this.showMode(-1);
19224                     this.fill();
19225                 }
19226                 break;
19227                 
19228             case 'td':
19229                 //Roo.log(className);
19230                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19231                     var day = parseInt(html, 10) || 1;
19232                     var year = this.viewDate.getUTCFullYear(),
19233                         month = this.viewDate.getUTCMonth();
19234
19235                     if (className.indexOf('old') > -1) {
19236                         if(month === 0 ){
19237                             month = 11;
19238                             year -= 1;
19239                         }else{
19240                             month -= 1;
19241                         }
19242                     } else if (className.indexOf('new') > -1) {
19243                         if (month == 11) {
19244                             month = 0;
19245                             year += 1;
19246                         } else {
19247                             month += 1;
19248                         }
19249                     }
19250                     //Roo.log([year,month,day]);
19251                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19252                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19253 //                    this.fill();
19254                     //Roo.log(this.formatDate(this.date));
19255                     this.setValue(this.formatDate(this.date));
19256                     this.hidePopup();
19257                 }
19258                 break;
19259         }
19260     },
19261     
19262     setStartDate: function(startDate)
19263     {
19264         this.startDate = startDate || -Infinity;
19265         if (this.startDate !== -Infinity) {
19266             this.startDate = this.parseDate(this.startDate);
19267         }
19268         this.update();
19269         this.updateNavArrows();
19270     },
19271
19272     setEndDate: function(endDate)
19273     {
19274         this.endDate = endDate || Infinity;
19275         if (this.endDate !== Infinity) {
19276             this.endDate = this.parseDate(this.endDate);
19277         }
19278         this.update();
19279         this.updateNavArrows();
19280     },
19281     
19282     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19283     {
19284         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19285         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19286             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19287         }
19288         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19289             return parseInt(d, 10);
19290         });
19291         this.update();
19292         this.updateNavArrows();
19293     },
19294     
19295     updateNavArrows: function() 
19296     {
19297         if(this.singleMode){
19298             return;
19299         }
19300         
19301         var d = new Date(this.viewDate),
19302         year = d.getUTCFullYear(),
19303         month = d.getUTCMonth();
19304         
19305         Roo.each(this.picker().select('.prev', true).elements, function(v){
19306             v.show();
19307             switch (this.viewMode) {
19308                 case 0:
19309
19310                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19311                         v.hide();
19312                     }
19313                     break;
19314                 case 1:
19315                 case 2:
19316                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19317                         v.hide();
19318                     }
19319                     break;
19320             }
19321         });
19322         
19323         Roo.each(this.picker().select('.next', true).elements, function(v){
19324             v.show();
19325             switch (this.viewMode) {
19326                 case 0:
19327
19328                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19329                         v.hide();
19330                     }
19331                     break;
19332                 case 1:
19333                 case 2:
19334                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19335                         v.hide();
19336                     }
19337                     break;
19338             }
19339         })
19340     },
19341     
19342     moveMonth: function(date, dir)
19343     {
19344         if (!dir) {
19345             return date;
19346         }
19347         var new_date = new Date(date.valueOf()),
19348         day = new_date.getUTCDate(),
19349         month = new_date.getUTCMonth(),
19350         mag = Math.abs(dir),
19351         new_month, test;
19352         dir = dir > 0 ? 1 : -1;
19353         if (mag == 1){
19354             test = dir == -1
19355             // If going back one month, make sure month is not current month
19356             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19357             ? function(){
19358                 return new_date.getUTCMonth() == month;
19359             }
19360             // If going forward one month, make sure month is as expected
19361             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19362             : function(){
19363                 return new_date.getUTCMonth() != new_month;
19364             };
19365             new_month = month + dir;
19366             new_date.setUTCMonth(new_month);
19367             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19368             if (new_month < 0 || new_month > 11) {
19369                 new_month = (new_month + 12) % 12;
19370             }
19371         } else {
19372             // For magnitudes >1, move one month at a time...
19373             for (var i=0; i<mag; i++) {
19374                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19375                 new_date = this.moveMonth(new_date, dir);
19376             }
19377             // ...then reset the day, keeping it in the new month
19378             new_month = new_date.getUTCMonth();
19379             new_date.setUTCDate(day);
19380             test = function(){
19381                 return new_month != new_date.getUTCMonth();
19382             };
19383         }
19384         // Common date-resetting loop -- if date is beyond end of month, make it
19385         // end of month
19386         while (test()){
19387             new_date.setUTCDate(--day);
19388             new_date.setUTCMonth(new_month);
19389         }
19390         return new_date;
19391     },
19392
19393     moveYear: function(date, dir)
19394     {
19395         return this.moveMonth(date, dir*12);
19396     },
19397
19398     dateWithinRange: function(date)
19399     {
19400         return date >= this.startDate && date <= this.endDate;
19401     },
19402
19403     
19404     remove: function() 
19405     {
19406         this.picker().remove();
19407     },
19408     
19409     validateValue : function(value)
19410     {
19411         if(this.getVisibilityEl().hasClass('hidden')){
19412             return true;
19413         }
19414         
19415         if(value.length < 1)  {
19416             if(this.allowBlank){
19417                 return true;
19418             }
19419             return false;
19420         }
19421         
19422         if(value.length < this.minLength){
19423             return false;
19424         }
19425         if(value.length > this.maxLength){
19426             return false;
19427         }
19428         if(this.vtype){
19429             var vt = Roo.form.VTypes;
19430             if(!vt[this.vtype](value, this)){
19431                 return false;
19432             }
19433         }
19434         if(typeof this.validator == "function"){
19435             var msg = this.validator(value);
19436             if(msg !== true){
19437                 return false;
19438             }
19439         }
19440         
19441         if(this.regex && !this.regex.test(value)){
19442             return false;
19443         }
19444         
19445         if(typeof(this.parseDate(value)) == 'undefined'){
19446             return false;
19447         }
19448         
19449         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19450             return false;
19451         }      
19452         
19453         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19454             return false;
19455         } 
19456         
19457         
19458         return true;
19459     },
19460     
19461     reset : function()
19462     {
19463         this.date = this.viewDate = '';
19464         
19465         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19466     }
19467    
19468 });
19469
19470 Roo.apply(Roo.bootstrap.DateField,  {
19471     
19472     head : {
19473         tag: 'thead',
19474         cn: [
19475         {
19476             tag: 'tr',
19477             cn: [
19478             {
19479                 tag: 'th',
19480                 cls: 'prev',
19481                 html: '<i class="fa fa-arrow-left"/>'
19482             },
19483             {
19484                 tag: 'th',
19485                 cls: 'switch',
19486                 colspan: '5'
19487             },
19488             {
19489                 tag: 'th',
19490                 cls: 'next',
19491                 html: '<i class="fa fa-arrow-right"/>'
19492             }
19493
19494             ]
19495         }
19496         ]
19497     },
19498     
19499     content : {
19500         tag: 'tbody',
19501         cn: [
19502         {
19503             tag: 'tr',
19504             cn: [
19505             {
19506                 tag: 'td',
19507                 colspan: '7'
19508             }
19509             ]
19510         }
19511         ]
19512     },
19513     
19514     footer : {
19515         tag: 'tfoot',
19516         cn: [
19517         {
19518             tag: 'tr',
19519             cn: [
19520             {
19521                 tag: 'th',
19522                 colspan: '7',
19523                 cls: 'today'
19524             }
19525                     
19526             ]
19527         }
19528         ]
19529     },
19530     
19531     dates:{
19532         en: {
19533             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19534             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19535             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19536             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19537             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19538             today: "Today"
19539         }
19540     },
19541     
19542     modes: [
19543     {
19544         clsName: 'days',
19545         navFnc: 'Month',
19546         navStep: 1
19547     },
19548     {
19549         clsName: 'months',
19550         navFnc: 'FullYear',
19551         navStep: 1
19552     },
19553     {
19554         clsName: 'years',
19555         navFnc: 'FullYear',
19556         navStep: 10
19557     }]
19558 });
19559
19560 Roo.apply(Roo.bootstrap.DateField,  {
19561   
19562     template : {
19563         tag: 'div',
19564         cls: 'datepicker dropdown-menu roo-dynamic',
19565         cn: [
19566         {
19567             tag: 'div',
19568             cls: 'datepicker-days',
19569             cn: [
19570             {
19571                 tag: 'table',
19572                 cls: 'table-condensed',
19573                 cn:[
19574                 Roo.bootstrap.DateField.head,
19575                 {
19576                     tag: 'tbody'
19577                 },
19578                 Roo.bootstrap.DateField.footer
19579                 ]
19580             }
19581             ]
19582         },
19583         {
19584             tag: 'div',
19585             cls: 'datepicker-months',
19586             cn: [
19587             {
19588                 tag: 'table',
19589                 cls: 'table-condensed',
19590                 cn:[
19591                 Roo.bootstrap.DateField.head,
19592                 Roo.bootstrap.DateField.content,
19593                 Roo.bootstrap.DateField.footer
19594                 ]
19595             }
19596             ]
19597         },
19598         {
19599             tag: 'div',
19600             cls: 'datepicker-years',
19601             cn: [
19602             {
19603                 tag: 'table',
19604                 cls: 'table-condensed',
19605                 cn:[
19606                 Roo.bootstrap.DateField.head,
19607                 Roo.bootstrap.DateField.content,
19608                 Roo.bootstrap.DateField.footer
19609                 ]
19610             }
19611             ]
19612         }
19613         ]
19614     }
19615 });
19616
19617  
19618
19619  /*
19620  * - LGPL
19621  *
19622  * TimeField
19623  * 
19624  */
19625
19626 /**
19627  * @class Roo.bootstrap.TimeField
19628  * @extends Roo.bootstrap.Input
19629  * Bootstrap DateField class
19630  * 
19631  * 
19632  * @constructor
19633  * Create a new TimeField
19634  * @param {Object} config The config object
19635  */
19636
19637 Roo.bootstrap.TimeField = function(config){
19638     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19639     this.addEvents({
19640             /**
19641              * @event show
19642              * Fires when this field show.
19643              * @param {Roo.bootstrap.DateField} thisthis
19644              * @param {Mixed} date The date value
19645              */
19646             show : true,
19647             /**
19648              * @event show
19649              * Fires when this field hide.
19650              * @param {Roo.bootstrap.DateField} this
19651              * @param {Mixed} date The date value
19652              */
19653             hide : true,
19654             /**
19655              * @event select
19656              * Fires when select a date.
19657              * @param {Roo.bootstrap.DateField} this
19658              * @param {Mixed} date The date value
19659              */
19660             select : true
19661         });
19662 };
19663
19664 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19665     
19666     /**
19667      * @cfg {String} format
19668      * The default time format string which can be overriden for localization support.  The format must be
19669      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19670      */
19671     format : "H:i",
19672        
19673     onRender: function(ct, position)
19674     {
19675         
19676         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19677                 
19678         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19679         
19680         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19681         
19682         this.pop = this.picker().select('>.datepicker-time',true).first();
19683         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19684         
19685         this.picker().on('mousedown', this.onMousedown, this);
19686         this.picker().on('click', this.onClick, this);
19687         
19688         this.picker().addClass('datepicker-dropdown');
19689     
19690         this.fillTime();
19691         this.update();
19692             
19693         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19694         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19695         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19696         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19697         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19698         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19699
19700     },
19701     
19702     fireKey: function(e){
19703         if (!this.picker().isVisible()){
19704             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19705                 this.show();
19706             }
19707             return;
19708         }
19709
19710         e.preventDefault();
19711         
19712         switch(e.keyCode){
19713             case 27: // escape
19714                 this.hide();
19715                 break;
19716             case 37: // left
19717             case 39: // right
19718                 this.onTogglePeriod();
19719                 break;
19720             case 38: // up
19721                 this.onIncrementMinutes();
19722                 break;
19723             case 40: // down
19724                 this.onDecrementMinutes();
19725                 break;
19726             case 13: // enter
19727             case 9: // tab
19728                 this.setTime();
19729                 break;
19730         }
19731     },
19732     
19733     onClick: function(e) {
19734         e.stopPropagation();
19735         e.preventDefault();
19736     },
19737     
19738     picker : function()
19739     {
19740         return this.el.select('.datepicker', true).first();
19741     },
19742     
19743     fillTime: function()
19744     {    
19745         var time = this.pop.select('tbody', true).first();
19746         
19747         time.dom.innerHTML = '';
19748         
19749         time.createChild({
19750             tag: 'tr',
19751             cn: [
19752                 {
19753                     tag: 'td',
19754                     cn: [
19755                         {
19756                             tag: 'a',
19757                             href: '#',
19758                             cls: 'btn',
19759                             cn: [
19760                                 {
19761                                     tag: 'span',
19762                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19763                                 }
19764                             ]
19765                         } 
19766                     ]
19767                 },
19768                 {
19769                     tag: 'td',
19770                     cls: 'separator'
19771                 },
19772                 {
19773                     tag: 'td',
19774                     cn: [
19775                         {
19776                             tag: 'a',
19777                             href: '#',
19778                             cls: 'btn',
19779                             cn: [
19780                                 {
19781                                     tag: 'span',
19782                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19783                                 }
19784                             ]
19785                         }
19786                     ]
19787                 },
19788                 {
19789                     tag: 'td',
19790                     cls: 'separator'
19791                 }
19792             ]
19793         });
19794         
19795         time.createChild({
19796             tag: 'tr',
19797             cn: [
19798                 {
19799                     tag: 'td',
19800                     cn: [
19801                         {
19802                             tag: 'span',
19803                             cls: 'timepicker-hour',
19804                             html: '00'
19805                         }  
19806                     ]
19807                 },
19808                 {
19809                     tag: 'td',
19810                     cls: 'separator',
19811                     html: ':'
19812                 },
19813                 {
19814                     tag: 'td',
19815                     cn: [
19816                         {
19817                             tag: 'span',
19818                             cls: 'timepicker-minute',
19819                             html: '00'
19820                         }  
19821                     ]
19822                 },
19823                 {
19824                     tag: 'td',
19825                     cls: 'separator'
19826                 },
19827                 {
19828                     tag: 'td',
19829                     cn: [
19830                         {
19831                             tag: 'button',
19832                             type: 'button',
19833                             cls: 'btn btn-primary period',
19834                             html: 'AM'
19835                             
19836                         }
19837                     ]
19838                 }
19839             ]
19840         });
19841         
19842         time.createChild({
19843             tag: 'tr',
19844             cn: [
19845                 {
19846                     tag: 'td',
19847                     cn: [
19848                         {
19849                             tag: 'a',
19850                             href: '#',
19851                             cls: 'btn',
19852                             cn: [
19853                                 {
19854                                     tag: 'span',
19855                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19856                                 }
19857                             ]
19858                         }
19859                     ]
19860                 },
19861                 {
19862                     tag: 'td',
19863                     cls: 'separator'
19864                 },
19865                 {
19866                     tag: 'td',
19867                     cn: [
19868                         {
19869                             tag: 'a',
19870                             href: '#',
19871                             cls: 'btn',
19872                             cn: [
19873                                 {
19874                                     tag: 'span',
19875                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19876                                 }
19877                             ]
19878                         }
19879                     ]
19880                 },
19881                 {
19882                     tag: 'td',
19883                     cls: 'separator'
19884                 }
19885             ]
19886         });
19887         
19888     },
19889     
19890     update: function()
19891     {
19892         
19893         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19894         
19895         this.fill();
19896     },
19897     
19898     fill: function() 
19899     {
19900         var hours = this.time.getHours();
19901         var minutes = this.time.getMinutes();
19902         var period = 'AM';
19903         
19904         if(hours > 11){
19905             period = 'PM';
19906         }
19907         
19908         if(hours == 0){
19909             hours = 12;
19910         }
19911         
19912         
19913         if(hours > 12){
19914             hours = hours - 12;
19915         }
19916         
19917         if(hours < 10){
19918             hours = '0' + hours;
19919         }
19920         
19921         if(minutes < 10){
19922             minutes = '0' + minutes;
19923         }
19924         
19925         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19926         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19927         this.pop.select('button', true).first().dom.innerHTML = period;
19928         
19929     },
19930     
19931     place: function()
19932     {   
19933         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19934         
19935         var cls = ['bottom'];
19936         
19937         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19938             cls.pop();
19939             cls.push('top');
19940         }
19941         
19942         cls.push('right');
19943         
19944         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19945             cls.pop();
19946             cls.push('left');
19947         }
19948         
19949         this.picker().addClass(cls.join('-'));
19950         
19951         var _this = this;
19952         
19953         Roo.each(cls, function(c){
19954             if(c == 'bottom'){
19955                 _this.picker().setTop(_this.inputEl().getHeight());
19956                 return;
19957             }
19958             if(c == 'top'){
19959                 _this.picker().setTop(0 - _this.picker().getHeight());
19960                 return;
19961             }
19962             
19963             if(c == 'left'){
19964                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19965                 return;
19966             }
19967             if(c == 'right'){
19968                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19969                 return;
19970             }
19971         });
19972         
19973     },
19974   
19975     onFocus : function()
19976     {
19977         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19978         this.show();
19979     },
19980     
19981     onBlur : function()
19982     {
19983         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19984         this.hide();
19985     },
19986     
19987     show : function()
19988     {
19989         this.picker().show();
19990         this.pop.show();
19991         this.update();
19992         this.place();
19993         
19994         this.fireEvent('show', this, this.date);
19995     },
19996     
19997     hide : function()
19998     {
19999         this.picker().hide();
20000         this.pop.hide();
20001         
20002         this.fireEvent('hide', this, this.date);
20003     },
20004     
20005     setTime : function()
20006     {
20007         this.hide();
20008         this.setValue(this.time.format(this.format));
20009         
20010         this.fireEvent('select', this, this.date);
20011         
20012         
20013     },
20014     
20015     onMousedown: function(e){
20016         e.stopPropagation();
20017         e.preventDefault();
20018     },
20019     
20020     onIncrementHours: function()
20021     {
20022         Roo.log('onIncrementHours');
20023         this.time = this.time.add(Date.HOUR, 1);
20024         this.update();
20025         
20026     },
20027     
20028     onDecrementHours: function()
20029     {
20030         Roo.log('onDecrementHours');
20031         this.time = this.time.add(Date.HOUR, -1);
20032         this.update();
20033     },
20034     
20035     onIncrementMinutes: function()
20036     {
20037         Roo.log('onIncrementMinutes');
20038         this.time = this.time.add(Date.MINUTE, 1);
20039         this.update();
20040     },
20041     
20042     onDecrementMinutes: function()
20043     {
20044         Roo.log('onDecrementMinutes');
20045         this.time = this.time.add(Date.MINUTE, -1);
20046         this.update();
20047     },
20048     
20049     onTogglePeriod: function()
20050     {
20051         Roo.log('onTogglePeriod');
20052         this.time = this.time.add(Date.HOUR, 12);
20053         this.update();
20054     }
20055     
20056    
20057 });
20058
20059 Roo.apply(Roo.bootstrap.TimeField,  {
20060     
20061     content : {
20062         tag: 'tbody',
20063         cn: [
20064             {
20065                 tag: 'tr',
20066                 cn: [
20067                 {
20068                     tag: 'td',
20069                     colspan: '7'
20070                 }
20071                 ]
20072             }
20073         ]
20074     },
20075     
20076     footer : {
20077         tag: 'tfoot',
20078         cn: [
20079             {
20080                 tag: 'tr',
20081                 cn: [
20082                 {
20083                     tag: 'th',
20084                     colspan: '7',
20085                     cls: '',
20086                     cn: [
20087                         {
20088                             tag: 'button',
20089                             cls: 'btn btn-info ok',
20090                             html: 'OK'
20091                         }
20092                     ]
20093                 }
20094
20095                 ]
20096             }
20097         ]
20098     }
20099 });
20100
20101 Roo.apply(Roo.bootstrap.TimeField,  {
20102   
20103     template : {
20104         tag: 'div',
20105         cls: 'datepicker dropdown-menu',
20106         cn: [
20107             {
20108                 tag: 'div',
20109                 cls: 'datepicker-time',
20110                 cn: [
20111                 {
20112                     tag: 'table',
20113                     cls: 'table-condensed',
20114                     cn:[
20115                     Roo.bootstrap.TimeField.content,
20116                     Roo.bootstrap.TimeField.footer
20117                     ]
20118                 }
20119                 ]
20120             }
20121         ]
20122     }
20123 });
20124
20125  
20126
20127  /*
20128  * - LGPL
20129  *
20130  * MonthField
20131  * 
20132  */
20133
20134 /**
20135  * @class Roo.bootstrap.MonthField
20136  * @extends Roo.bootstrap.Input
20137  * Bootstrap MonthField class
20138  * 
20139  * @cfg {String} language default en
20140  * 
20141  * @constructor
20142  * Create a new MonthField
20143  * @param {Object} config The config object
20144  */
20145
20146 Roo.bootstrap.MonthField = function(config){
20147     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20148     
20149     this.addEvents({
20150         /**
20151          * @event show
20152          * Fires when this field show.
20153          * @param {Roo.bootstrap.MonthField} this
20154          * @param {Mixed} date The date value
20155          */
20156         show : true,
20157         /**
20158          * @event show
20159          * Fires when this field hide.
20160          * @param {Roo.bootstrap.MonthField} this
20161          * @param {Mixed} date The date value
20162          */
20163         hide : true,
20164         /**
20165          * @event select
20166          * Fires when select a date.
20167          * @param {Roo.bootstrap.MonthField} this
20168          * @param {String} oldvalue The old value
20169          * @param {String} newvalue The new value
20170          */
20171         select : true
20172     });
20173 };
20174
20175 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20176     
20177     onRender: function(ct, position)
20178     {
20179         
20180         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20181         
20182         this.language = this.language || 'en';
20183         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20184         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20185         
20186         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20187         this.isInline = false;
20188         this.isInput = true;
20189         this.component = this.el.select('.add-on', true).first() || false;
20190         this.component = (this.component && this.component.length === 0) ? false : this.component;
20191         this.hasInput = this.component && this.inputEL().length;
20192         
20193         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20194         
20195         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20196         
20197         this.picker().on('mousedown', this.onMousedown, this);
20198         this.picker().on('click', this.onClick, this);
20199         
20200         this.picker().addClass('datepicker-dropdown');
20201         
20202         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20203             v.setStyle('width', '189px');
20204         });
20205         
20206         this.fillMonths();
20207         
20208         this.update();
20209         
20210         if(this.isInline) {
20211             this.show();
20212         }
20213         
20214     },
20215     
20216     setValue: function(v, suppressEvent)
20217     {   
20218         var o = this.getValue();
20219         
20220         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20221         
20222         this.update();
20223
20224         if(suppressEvent !== true){
20225             this.fireEvent('select', this, o, v);
20226         }
20227         
20228     },
20229     
20230     getValue: function()
20231     {
20232         return this.value;
20233     },
20234     
20235     onClick: function(e) 
20236     {
20237         e.stopPropagation();
20238         e.preventDefault();
20239         
20240         var target = e.getTarget();
20241         
20242         if(target.nodeName.toLowerCase() === 'i'){
20243             target = Roo.get(target).dom.parentNode;
20244         }
20245         
20246         var nodeName = target.nodeName;
20247         var className = target.className;
20248         var html = target.innerHTML;
20249         
20250         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20251             return;
20252         }
20253         
20254         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20255         
20256         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20257         
20258         this.hide();
20259                         
20260     },
20261     
20262     picker : function()
20263     {
20264         return this.pickerEl;
20265     },
20266     
20267     fillMonths: function()
20268     {    
20269         var i = 0;
20270         var months = this.picker().select('>.datepicker-months td', true).first();
20271         
20272         months.dom.innerHTML = '';
20273         
20274         while (i < 12) {
20275             var month = {
20276                 tag: 'span',
20277                 cls: 'month',
20278                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20279             };
20280             
20281             months.createChild(month);
20282         }
20283         
20284     },
20285     
20286     update: function()
20287     {
20288         var _this = this;
20289         
20290         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20291             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20292         }
20293         
20294         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20295             e.removeClass('active');
20296             
20297             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20298                 e.addClass('active');
20299             }
20300         })
20301     },
20302     
20303     place: function()
20304     {
20305         if(this.isInline) {
20306             return;
20307         }
20308         
20309         this.picker().removeClass(['bottom', 'top']);
20310         
20311         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20312             /*
20313              * place to the top of element!
20314              *
20315              */
20316             
20317             this.picker().addClass('top');
20318             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20319             
20320             return;
20321         }
20322         
20323         this.picker().addClass('bottom');
20324         
20325         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20326     },
20327     
20328     onFocus : function()
20329     {
20330         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20331         this.show();
20332     },
20333     
20334     onBlur : function()
20335     {
20336         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20337         
20338         var d = this.inputEl().getValue();
20339         
20340         this.setValue(d);
20341                 
20342         this.hide();
20343     },
20344     
20345     show : function()
20346     {
20347         this.picker().show();
20348         this.picker().select('>.datepicker-months', true).first().show();
20349         this.update();
20350         this.place();
20351         
20352         this.fireEvent('show', this, this.date);
20353     },
20354     
20355     hide : function()
20356     {
20357         if(this.isInline) {
20358             return;
20359         }
20360         this.picker().hide();
20361         this.fireEvent('hide', this, this.date);
20362         
20363     },
20364     
20365     onMousedown: function(e)
20366     {
20367         e.stopPropagation();
20368         e.preventDefault();
20369     },
20370     
20371     keyup: function(e)
20372     {
20373         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20374         this.update();
20375     },
20376
20377     fireKey: function(e)
20378     {
20379         if (!this.picker().isVisible()){
20380             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20381                 this.show();
20382             }
20383             return;
20384         }
20385         
20386         var dir;
20387         
20388         switch(e.keyCode){
20389             case 27: // escape
20390                 this.hide();
20391                 e.preventDefault();
20392                 break;
20393             case 37: // left
20394             case 39: // right
20395                 dir = e.keyCode == 37 ? -1 : 1;
20396                 
20397                 this.vIndex = this.vIndex + dir;
20398                 
20399                 if(this.vIndex < 0){
20400                     this.vIndex = 0;
20401                 }
20402                 
20403                 if(this.vIndex > 11){
20404                     this.vIndex = 11;
20405                 }
20406                 
20407                 if(isNaN(this.vIndex)){
20408                     this.vIndex = 0;
20409                 }
20410                 
20411                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20412                 
20413                 break;
20414             case 38: // up
20415             case 40: // down
20416                 
20417                 dir = e.keyCode == 38 ? -1 : 1;
20418                 
20419                 this.vIndex = this.vIndex + dir * 4;
20420                 
20421                 if(this.vIndex < 0){
20422                     this.vIndex = 0;
20423                 }
20424                 
20425                 if(this.vIndex > 11){
20426                     this.vIndex = 11;
20427                 }
20428                 
20429                 if(isNaN(this.vIndex)){
20430                     this.vIndex = 0;
20431                 }
20432                 
20433                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20434                 break;
20435                 
20436             case 13: // enter
20437                 
20438                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20439                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20440                 }
20441                 
20442                 this.hide();
20443                 e.preventDefault();
20444                 break;
20445             case 9: // tab
20446                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20447                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20448                 }
20449                 this.hide();
20450                 break;
20451             case 16: // shift
20452             case 17: // ctrl
20453             case 18: // alt
20454                 break;
20455             default :
20456                 this.hide();
20457                 
20458         }
20459     },
20460     
20461     remove: function() 
20462     {
20463         this.picker().remove();
20464     }
20465    
20466 });
20467
20468 Roo.apply(Roo.bootstrap.MonthField,  {
20469     
20470     content : {
20471         tag: 'tbody',
20472         cn: [
20473         {
20474             tag: 'tr',
20475             cn: [
20476             {
20477                 tag: 'td',
20478                 colspan: '7'
20479             }
20480             ]
20481         }
20482         ]
20483     },
20484     
20485     dates:{
20486         en: {
20487             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20488             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20489         }
20490     }
20491 });
20492
20493 Roo.apply(Roo.bootstrap.MonthField,  {
20494   
20495     template : {
20496         tag: 'div',
20497         cls: 'datepicker dropdown-menu roo-dynamic',
20498         cn: [
20499             {
20500                 tag: 'div',
20501                 cls: 'datepicker-months',
20502                 cn: [
20503                 {
20504                     tag: 'table',
20505                     cls: 'table-condensed',
20506                     cn:[
20507                         Roo.bootstrap.DateField.content
20508                     ]
20509                 }
20510                 ]
20511             }
20512         ]
20513     }
20514 });
20515
20516  
20517
20518  
20519  /*
20520  * - LGPL
20521  *
20522  * CheckBox
20523  * 
20524  */
20525
20526 /**
20527  * @class Roo.bootstrap.CheckBox
20528  * @extends Roo.bootstrap.Input
20529  * Bootstrap CheckBox class
20530  * 
20531  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20532  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20533  * @cfg {String} boxLabel The text that appears beside the checkbox
20534  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20535  * @cfg {Boolean} checked initnal the element
20536  * @cfg {Boolean} inline inline the element (default false)
20537  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20538  * @cfg {String} tooltip label tooltip
20539  * 
20540  * @constructor
20541  * Create a new CheckBox
20542  * @param {Object} config The config object
20543  */
20544
20545 Roo.bootstrap.CheckBox = function(config){
20546     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20547    
20548     this.addEvents({
20549         /**
20550         * @event check
20551         * Fires when the element is checked or unchecked.
20552         * @param {Roo.bootstrap.CheckBox} this This input
20553         * @param {Boolean} checked The new checked value
20554         */
20555        check : true,
20556        /**
20557         * @event click
20558         * Fires when the element is click.
20559         * @param {Roo.bootstrap.CheckBox} this This input
20560         */
20561        click : true
20562     });
20563     
20564 };
20565
20566 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20567   
20568     inputType: 'checkbox',
20569     inputValue: 1,
20570     valueOff: 0,
20571     boxLabel: false,
20572     checked: false,
20573     weight : false,
20574     inline: false,
20575     tooltip : '',
20576     
20577     getAutoCreate : function()
20578     {
20579         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20580         
20581         var id = Roo.id();
20582         
20583         var cfg = {};
20584         
20585         cfg.cls = 'form-group ' + this.inputType; //input-group
20586         
20587         if(this.inline){
20588             cfg.cls += ' ' + this.inputType + '-inline';
20589         }
20590         
20591         var input =  {
20592             tag: 'input',
20593             id : id,
20594             type : this.inputType,
20595             value : this.inputValue,
20596             cls : 'roo-' + this.inputType, //'form-box',
20597             placeholder : this.placeholder || ''
20598             
20599         };
20600         
20601         if(this.inputType != 'radio'){
20602             var hidden =  {
20603                 tag: 'input',
20604                 type : 'hidden',
20605                 cls : 'roo-hidden-value',
20606                 value : this.checked ? this.inputValue : this.valueOff
20607             };
20608         }
20609         
20610             
20611         if (this.weight) { // Validity check?
20612             cfg.cls += " " + this.inputType + "-" + this.weight;
20613         }
20614         
20615         if (this.disabled) {
20616             input.disabled=true;
20617         }
20618         
20619         if(this.checked){
20620             input.checked = this.checked;
20621         }
20622         
20623         if (this.name) {
20624             
20625             input.name = this.name;
20626             
20627             if(this.inputType != 'radio'){
20628                 hidden.name = this.name;
20629                 input.name = '_hidden_' + this.name;
20630             }
20631         }
20632         
20633         if (this.size) {
20634             input.cls += ' input-' + this.size;
20635         }
20636         
20637         var settings=this;
20638         
20639         ['xs','sm','md','lg'].map(function(size){
20640             if (settings[size]) {
20641                 cfg.cls += ' col-' + size + '-' + settings[size];
20642             }
20643         });
20644         
20645         var inputblock = input;
20646          
20647         if (this.before || this.after) {
20648             
20649             inputblock = {
20650                 cls : 'input-group',
20651                 cn :  [] 
20652             };
20653             
20654             if (this.before) {
20655                 inputblock.cn.push({
20656                     tag :'span',
20657                     cls : 'input-group-addon',
20658                     html : this.before
20659                 });
20660             }
20661             
20662             inputblock.cn.push(input);
20663             
20664             if(this.inputType != 'radio'){
20665                 inputblock.cn.push(hidden);
20666             }
20667             
20668             if (this.after) {
20669                 inputblock.cn.push({
20670                     tag :'span',
20671                     cls : 'input-group-addon',
20672                     html : this.after
20673                 });
20674             }
20675             
20676         }
20677         
20678         if (align ==='left' && this.fieldLabel.length) {
20679 //                Roo.log("left and has label");
20680             cfg.cn = [
20681                 {
20682                     tag: 'label',
20683                     'for' :  id,
20684                     cls : 'control-label',
20685                     html : this.fieldLabel
20686                 },
20687                 {
20688                     cls : "", 
20689                     cn: [
20690                         inputblock
20691                     ]
20692                 }
20693             ];
20694             
20695             if(this.labelWidth > 12){
20696                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20697             }
20698             
20699             if(this.labelWidth < 13 && this.labelmd == 0){
20700                 this.labelmd = this.labelWidth;
20701             }
20702             
20703             if(this.labellg > 0){
20704                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20705                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20706             }
20707             
20708             if(this.labelmd > 0){
20709                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20710                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20711             }
20712             
20713             if(this.labelsm > 0){
20714                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20715                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20716             }
20717             
20718             if(this.labelxs > 0){
20719                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20720                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20721             }
20722             
20723         } else if ( this.fieldLabel.length) {
20724 //                Roo.log(" label");
20725                 cfg.cn = [
20726                    
20727                     {
20728                         tag: this.boxLabel ? 'span' : 'label',
20729                         'for': id,
20730                         cls: 'control-label box-input-label',
20731                         //cls : 'input-group-addon',
20732                         html : this.fieldLabel
20733                     },
20734                     
20735                     inputblock
20736                     
20737                 ];
20738
20739         } else {
20740             
20741 //                Roo.log(" no label && no align");
20742                 cfg.cn = [  inputblock ] ;
20743                 
20744                 
20745         }
20746         
20747         if(this.boxLabel){
20748              var boxLabelCfg = {
20749                 tag: 'label',
20750                 //'for': id, // box label is handled by onclick - so no for...
20751                 cls: 'box-label',
20752                 html: this.boxLabel
20753             };
20754             
20755             if(this.tooltip){
20756                 boxLabelCfg.tooltip = this.tooltip;
20757             }
20758              
20759             cfg.cn.push(boxLabelCfg);
20760         }
20761         
20762         if(this.inputType != 'radio'){
20763             cfg.cn.push(hidden);
20764         }
20765         
20766         return cfg;
20767         
20768     },
20769     
20770     /**
20771      * return the real input element.
20772      */
20773     inputEl: function ()
20774     {
20775         return this.el.select('input.roo-' + this.inputType,true).first();
20776     },
20777     hiddenEl: function ()
20778     {
20779         return this.el.select('input.roo-hidden-value',true).first();
20780     },
20781     
20782     labelEl: function()
20783     {
20784         return this.el.select('label.control-label',true).first();
20785     },
20786     /* depricated... */
20787     
20788     label: function()
20789     {
20790         return this.labelEl();
20791     },
20792     
20793     boxLabelEl: function()
20794     {
20795         return this.el.select('label.box-label',true).first();
20796     },
20797     
20798     initEvents : function()
20799     {
20800 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20801         
20802         this.inputEl().on('click', this.onClick,  this);
20803         
20804         if (this.boxLabel) { 
20805             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20806         }
20807         
20808         this.startValue = this.getValue();
20809         
20810         if(this.groupId){
20811             Roo.bootstrap.CheckBox.register(this);
20812         }
20813     },
20814     
20815     onClick : function(e)
20816     {   
20817         if(this.fireEvent('click', this, e) !== false){
20818             this.setChecked(!this.checked);
20819         }
20820         
20821     },
20822     
20823     setChecked : function(state,suppressEvent)
20824     {
20825         this.startValue = this.getValue();
20826
20827         if(this.inputType == 'radio'){
20828             
20829             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20830                 e.dom.checked = false;
20831             });
20832             
20833             this.inputEl().dom.checked = true;
20834             
20835             this.inputEl().dom.value = this.inputValue;
20836             
20837             if(suppressEvent !== true){
20838                 this.fireEvent('check', this, true);
20839             }
20840             
20841             this.validate();
20842             
20843             return;
20844         }
20845         
20846         this.checked = state;
20847         
20848         this.inputEl().dom.checked = state;
20849         
20850         
20851         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20852         
20853         if(suppressEvent !== true){
20854             this.fireEvent('check', this, state);
20855         }
20856         
20857         this.validate();
20858     },
20859     
20860     getValue : function()
20861     {
20862         if(this.inputType == 'radio'){
20863             return this.getGroupValue();
20864         }
20865         
20866         return this.hiddenEl().dom.value;
20867         
20868     },
20869     
20870     getGroupValue : function()
20871     {
20872         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20873             return '';
20874         }
20875         
20876         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20877     },
20878     
20879     setValue : function(v,suppressEvent)
20880     {
20881         if(this.inputType == 'radio'){
20882             this.setGroupValue(v, suppressEvent);
20883             return;
20884         }
20885         
20886         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20887         
20888         this.validate();
20889     },
20890     
20891     setGroupValue : function(v, suppressEvent)
20892     {
20893         this.startValue = this.getValue();
20894         
20895         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20896             e.dom.checked = false;
20897             
20898             if(e.dom.value == v){
20899                 e.dom.checked = true;
20900             }
20901         });
20902         
20903         if(suppressEvent !== true){
20904             this.fireEvent('check', this, true);
20905         }
20906
20907         this.validate();
20908         
20909         return;
20910     },
20911     
20912     validate : function()
20913     {
20914         if(this.getVisibilityEl().hasClass('hidden')){
20915             return true;
20916         }
20917         
20918         if(
20919                 this.disabled || 
20920                 (this.inputType == 'radio' && this.validateRadio()) ||
20921                 (this.inputType == 'checkbox' && this.validateCheckbox())
20922         ){
20923             this.markValid();
20924             return true;
20925         }
20926         
20927         this.markInvalid();
20928         return false;
20929     },
20930     
20931     validateRadio : function()
20932     {
20933         if(this.getVisibilityEl().hasClass('hidden')){
20934             return true;
20935         }
20936         
20937         if(this.allowBlank){
20938             return true;
20939         }
20940         
20941         var valid = false;
20942         
20943         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944             if(!e.dom.checked){
20945                 return;
20946             }
20947             
20948             valid = true;
20949             
20950             return false;
20951         });
20952         
20953         return valid;
20954     },
20955     
20956     validateCheckbox : function()
20957     {
20958         if(!this.groupId){
20959             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20960             //return (this.getValue() == this.inputValue) ? true : false;
20961         }
20962         
20963         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20964         
20965         if(!group){
20966             return false;
20967         }
20968         
20969         var r = false;
20970         
20971         for(var i in group){
20972             if(group[i].el.isVisible(true)){
20973                 r = false;
20974                 break;
20975             }
20976             
20977             r = true;
20978         }
20979         
20980         for(var i in group){
20981             if(r){
20982                 break;
20983             }
20984             
20985             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20986         }
20987         
20988         return r;
20989     },
20990     
20991     /**
20992      * Mark this field as valid
20993      */
20994     markValid : function()
20995     {
20996         var _this = this;
20997         
20998         this.fireEvent('valid', this);
20999         
21000         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21001         
21002         if(this.groupId){
21003             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21004         }
21005         
21006         if(label){
21007             label.markValid();
21008         }
21009
21010         if(this.inputType == 'radio'){
21011             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21012                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21013                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21014             });
21015             
21016             return;
21017         }
21018
21019         if(!this.groupId){
21020             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21021             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21022             return;
21023         }
21024         
21025         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21026         
21027         if(!group){
21028             return;
21029         }
21030         
21031         for(var i in group){
21032             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21033             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21034         }
21035     },
21036     
21037      /**
21038      * Mark this field as invalid
21039      * @param {String} msg The validation message
21040      */
21041     markInvalid : function(msg)
21042     {
21043         if(this.allowBlank){
21044             return;
21045         }
21046         
21047         var _this = this;
21048         
21049         this.fireEvent('invalid', this, msg);
21050         
21051         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21052         
21053         if(this.groupId){
21054             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21055         }
21056         
21057         if(label){
21058             label.markInvalid();
21059         }
21060             
21061         if(this.inputType == 'radio'){
21062             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21063                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21064                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21065             });
21066             
21067             return;
21068         }
21069         
21070         if(!this.groupId){
21071             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21072             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21073             return;
21074         }
21075         
21076         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21077         
21078         if(!group){
21079             return;
21080         }
21081         
21082         for(var i in group){
21083             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21084             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21085         }
21086         
21087     },
21088     
21089     clearInvalid : function()
21090     {
21091         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21092         
21093         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21094         
21095         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21096         
21097         if (label && label.iconEl) {
21098             label.iconEl.removeClass(label.validClass);
21099             label.iconEl.removeClass(label.invalidClass);
21100         }
21101     },
21102     
21103     disable : function()
21104     {
21105         if(this.inputType != 'radio'){
21106             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21107             return;
21108         }
21109         
21110         var _this = this;
21111         
21112         if(this.rendered){
21113             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21114                 _this.getActionEl().addClass(this.disabledClass);
21115                 e.dom.disabled = true;
21116             });
21117         }
21118         
21119         this.disabled = true;
21120         this.fireEvent("disable", this);
21121         return this;
21122     },
21123
21124     enable : function()
21125     {
21126         if(this.inputType != 'radio'){
21127             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21128             return;
21129         }
21130         
21131         var _this = this;
21132         
21133         if(this.rendered){
21134             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21135                 _this.getActionEl().removeClass(this.disabledClass);
21136                 e.dom.disabled = false;
21137             });
21138         }
21139         
21140         this.disabled = false;
21141         this.fireEvent("enable", this);
21142         return this;
21143     },
21144     
21145     setBoxLabel : function(v)
21146     {
21147         this.boxLabel = v;
21148         
21149         if(this.rendered){
21150             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21151         }
21152     }
21153
21154 });
21155
21156 Roo.apply(Roo.bootstrap.CheckBox, {
21157     
21158     groups: {},
21159     
21160      /**
21161     * register a CheckBox Group
21162     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21163     */
21164     register : function(checkbox)
21165     {
21166         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21167             this.groups[checkbox.groupId] = {};
21168         }
21169         
21170         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21171             return;
21172         }
21173         
21174         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21175         
21176     },
21177     /**
21178     * fetch a CheckBox Group based on the group ID
21179     * @param {string} the group ID
21180     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21181     */
21182     get: function(groupId) {
21183         if (typeof(this.groups[groupId]) == 'undefined') {
21184             return false;
21185         }
21186         
21187         return this.groups[groupId] ;
21188     }
21189     
21190     
21191 });
21192 /*
21193  * - LGPL
21194  *
21195  * RadioItem
21196  * 
21197  */
21198
21199 /**
21200  * @class Roo.bootstrap.Radio
21201  * @extends Roo.bootstrap.Component
21202  * Bootstrap Radio class
21203  * @cfg {String} boxLabel - the label associated
21204  * @cfg {String} value - the value of radio
21205  * 
21206  * @constructor
21207  * Create a new Radio
21208  * @param {Object} config The config object
21209  */
21210 Roo.bootstrap.Radio = function(config){
21211     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21212     
21213 };
21214
21215 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21216     
21217     boxLabel : '',
21218     
21219     value : '',
21220     
21221     getAutoCreate : function()
21222     {
21223         var cfg = {
21224             tag : 'div',
21225             cls : 'form-group radio',
21226             cn : [
21227                 {
21228                     tag : 'label',
21229                     cls : 'box-label',
21230                     html : this.boxLabel
21231                 }
21232             ]
21233         };
21234         
21235         return cfg;
21236     },
21237     
21238     initEvents : function() 
21239     {
21240         this.parent().register(this);
21241         
21242         this.el.on('click', this.onClick, this);
21243         
21244     },
21245     
21246     onClick : function(e)
21247     {
21248         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21249             this.setChecked(true);
21250         }
21251     },
21252     
21253     setChecked : function(state, suppressEvent)
21254     {
21255         this.parent().setValue(this.value, suppressEvent);
21256         
21257     },
21258     
21259     setBoxLabel : function(v)
21260     {
21261         this.boxLabel = v;
21262         
21263         if(this.rendered){
21264             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21265         }
21266     }
21267     
21268 });
21269  
21270
21271  /*
21272  * - LGPL
21273  *
21274  * Input
21275  * 
21276  */
21277
21278 /**
21279  * @class Roo.bootstrap.SecurePass
21280  * @extends Roo.bootstrap.Input
21281  * Bootstrap SecurePass class
21282  *
21283  * 
21284  * @constructor
21285  * Create a new SecurePass
21286  * @param {Object} config The config object
21287  */
21288  
21289 Roo.bootstrap.SecurePass = function (config) {
21290     // these go here, so the translation tool can replace them..
21291     this.errors = {
21292         PwdEmpty: "Please type a password, and then retype it to confirm.",
21293         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21294         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21295         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21296         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21297         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21298         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21299         TooWeak: "Your password is Too Weak."
21300     },
21301     this.meterLabel = "Password strength:";
21302     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21303     this.meterClass = [
21304         "roo-password-meter-tooweak", 
21305         "roo-password-meter-weak", 
21306         "roo-password-meter-medium", 
21307         "roo-password-meter-strong", 
21308         "roo-password-meter-grey"
21309     ];
21310     
21311     this.errors = {};
21312     
21313     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21314 }
21315
21316 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21317     /**
21318      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21319      * {
21320      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21321      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21322      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21323      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21324      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21325      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21326      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21327      * })
21328      */
21329     // private
21330     
21331     meterWidth: 300,
21332     errorMsg :'',    
21333     errors: false,
21334     imageRoot: '/',
21335     /**
21336      * @cfg {String/Object} Label for the strength meter (defaults to
21337      * 'Password strength:')
21338      */
21339     // private
21340     meterLabel: '',
21341     /**
21342      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21343      * ['Weak', 'Medium', 'Strong'])
21344      */
21345     // private    
21346     pwdStrengths: false,    
21347     // private
21348     strength: 0,
21349     // private
21350     _lastPwd: null,
21351     // private
21352     kCapitalLetter: 0,
21353     kSmallLetter: 1,
21354     kDigit: 2,
21355     kPunctuation: 3,
21356     
21357     insecure: false,
21358     // private
21359     initEvents: function ()
21360     {
21361         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21362
21363         if (this.el.is('input[type=password]') && Roo.isSafari) {
21364             this.el.on('keydown', this.SafariOnKeyDown, this);
21365         }
21366
21367         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21368     },
21369     // private
21370     onRender: function (ct, position)
21371     {
21372         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21373         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21374         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21375
21376         this.trigger.createChild({
21377                    cn: [
21378                     {
21379                     //id: 'PwdMeter',
21380                     tag: 'div',
21381                     cls: 'roo-password-meter-grey col-xs-12',
21382                     style: {
21383                         //width: 0,
21384                         //width: this.meterWidth + 'px'                                                
21385                         }
21386                     },
21387                     {                            
21388                          cls: 'roo-password-meter-text'                          
21389                     }
21390                 ]            
21391         });
21392
21393          
21394         if (this.hideTrigger) {
21395             this.trigger.setDisplayed(false);
21396         }
21397         this.setSize(this.width || '', this.height || '');
21398     },
21399     // private
21400     onDestroy: function ()
21401     {
21402         if (this.trigger) {
21403             this.trigger.removeAllListeners();
21404             this.trigger.remove();
21405         }
21406         if (this.wrap) {
21407             this.wrap.remove();
21408         }
21409         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21410     },
21411     // private
21412     checkStrength: function ()
21413     {
21414         var pwd = this.inputEl().getValue();
21415         if (pwd == this._lastPwd) {
21416             return;
21417         }
21418
21419         var strength;
21420         if (this.ClientSideStrongPassword(pwd)) {
21421             strength = 3;
21422         } else if (this.ClientSideMediumPassword(pwd)) {
21423             strength = 2;
21424         } else if (this.ClientSideWeakPassword(pwd)) {
21425             strength = 1;
21426         } else {
21427             strength = 0;
21428         }
21429         
21430         Roo.log('strength1: ' + strength);
21431         
21432         //var pm = this.trigger.child('div/div/div').dom;
21433         var pm = this.trigger.child('div/div');
21434         pm.removeClass(this.meterClass);
21435         pm.addClass(this.meterClass[strength]);
21436                 
21437         
21438         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21439                 
21440         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21441         
21442         this._lastPwd = pwd;
21443     },
21444     reset: function ()
21445     {
21446         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21447         
21448         this._lastPwd = '';
21449         
21450         var pm = this.trigger.child('div/div');
21451         pm.removeClass(this.meterClass);
21452         pm.addClass('roo-password-meter-grey');        
21453         
21454         
21455         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21456         
21457         pt.innerHTML = '';
21458         this.inputEl().dom.type='password';
21459     },
21460     // private
21461     validateValue: function (value)
21462     {
21463         
21464         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21465             return false;
21466         }
21467         if (value.length == 0) {
21468             if (this.allowBlank) {
21469                 this.clearInvalid();
21470                 return true;
21471             }
21472
21473             this.markInvalid(this.errors.PwdEmpty);
21474             this.errorMsg = this.errors.PwdEmpty;
21475             return false;
21476         }
21477         
21478         if(this.insecure){
21479             return true;
21480         }
21481         
21482         if ('[\x21-\x7e]*'.match(value)) {
21483             this.markInvalid(this.errors.PwdBadChar);
21484             this.errorMsg = this.errors.PwdBadChar;
21485             return false;
21486         }
21487         if (value.length < 6) {
21488             this.markInvalid(this.errors.PwdShort);
21489             this.errorMsg = this.errors.PwdShort;
21490             return false;
21491         }
21492         if (value.length > 16) {
21493             this.markInvalid(this.errors.PwdLong);
21494             this.errorMsg = this.errors.PwdLong;
21495             return false;
21496         }
21497         var strength;
21498         if (this.ClientSideStrongPassword(value)) {
21499             strength = 3;
21500         } else if (this.ClientSideMediumPassword(value)) {
21501             strength = 2;
21502         } else if (this.ClientSideWeakPassword(value)) {
21503             strength = 1;
21504         } else {
21505             strength = 0;
21506         }
21507
21508         
21509         if (strength < 2) {
21510             //this.markInvalid(this.errors.TooWeak);
21511             this.errorMsg = this.errors.TooWeak;
21512             //return false;
21513         }
21514         
21515         
21516         console.log('strength2: ' + strength);
21517         
21518         //var pm = this.trigger.child('div/div/div').dom;
21519         
21520         var pm = this.trigger.child('div/div');
21521         pm.removeClass(this.meterClass);
21522         pm.addClass(this.meterClass[strength]);
21523                 
21524         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21525                 
21526         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21527         
21528         this.errorMsg = ''; 
21529         return true;
21530     },
21531     // private
21532     CharacterSetChecks: function (type)
21533     {
21534         this.type = type;
21535         this.fResult = false;
21536     },
21537     // private
21538     isctype: function (character, type)
21539     {
21540         switch (type) {  
21541             case this.kCapitalLetter:
21542                 if (character >= 'A' && character <= 'Z') {
21543                     return true;
21544                 }
21545                 break;
21546             
21547             case this.kSmallLetter:
21548                 if (character >= 'a' && character <= 'z') {
21549                     return true;
21550                 }
21551                 break;
21552             
21553             case this.kDigit:
21554                 if (character >= '0' && character <= '9') {
21555                     return true;
21556                 }
21557                 break;
21558             
21559             case this.kPunctuation:
21560                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21561                     return true;
21562                 }
21563                 break;
21564             
21565             default:
21566                 return false;
21567         }
21568
21569     },
21570     // private
21571     IsLongEnough: function (pwd, size)
21572     {
21573         return !(pwd == null || isNaN(size) || pwd.length < size);
21574     },
21575     // private
21576     SpansEnoughCharacterSets: function (word, nb)
21577     {
21578         if (!this.IsLongEnough(word, nb))
21579         {
21580             return false;
21581         }
21582
21583         var characterSetChecks = new Array(
21584             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21585             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21586         );
21587         
21588         for (var index = 0; index < word.length; ++index) {
21589             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21590                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21591                     characterSetChecks[nCharSet].fResult = true;
21592                     break;
21593                 }
21594             }
21595         }
21596
21597         var nCharSets = 0;
21598         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21599             if (characterSetChecks[nCharSet].fResult) {
21600                 ++nCharSets;
21601             }
21602         }
21603
21604         if (nCharSets < nb) {
21605             return false;
21606         }
21607         return true;
21608     },
21609     // private
21610     ClientSideStrongPassword: function (pwd)
21611     {
21612         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21613     },
21614     // private
21615     ClientSideMediumPassword: function (pwd)
21616     {
21617         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21618     },
21619     // private
21620     ClientSideWeakPassword: function (pwd)
21621     {
21622         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21623     }
21624           
21625 })//<script type="text/javascript">
21626
21627 /*
21628  * Based  Ext JS Library 1.1.1
21629  * Copyright(c) 2006-2007, Ext JS, LLC.
21630  * LGPL
21631  *
21632  */
21633  
21634 /**
21635  * @class Roo.HtmlEditorCore
21636  * @extends Roo.Component
21637  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21638  *
21639  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21640  */
21641
21642 Roo.HtmlEditorCore = function(config){
21643     
21644     
21645     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21646     
21647     
21648     this.addEvents({
21649         /**
21650          * @event initialize
21651          * Fires when the editor is fully initialized (including the iframe)
21652          * @param {Roo.HtmlEditorCore} this
21653          */
21654         initialize: true,
21655         /**
21656          * @event activate
21657          * Fires when the editor is first receives the focus. Any insertion must wait
21658          * until after this event.
21659          * @param {Roo.HtmlEditorCore} this
21660          */
21661         activate: true,
21662          /**
21663          * @event beforesync
21664          * Fires before the textarea is updated with content from the editor iframe. Return false
21665          * to cancel the sync.
21666          * @param {Roo.HtmlEditorCore} this
21667          * @param {String} html
21668          */
21669         beforesync: true,
21670          /**
21671          * @event beforepush
21672          * Fires before the iframe editor is updated with content from the textarea. Return false
21673          * to cancel the push.
21674          * @param {Roo.HtmlEditorCore} this
21675          * @param {String} html
21676          */
21677         beforepush: true,
21678          /**
21679          * @event sync
21680          * Fires when the textarea is updated with content from the editor iframe.
21681          * @param {Roo.HtmlEditorCore} this
21682          * @param {String} html
21683          */
21684         sync: true,
21685          /**
21686          * @event push
21687          * Fires when the iframe editor is updated with content from the textarea.
21688          * @param {Roo.HtmlEditorCore} this
21689          * @param {String} html
21690          */
21691         push: true,
21692         
21693         /**
21694          * @event editorevent
21695          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21696          * @param {Roo.HtmlEditorCore} this
21697          */
21698         editorevent: true
21699         
21700     });
21701     
21702     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21703     
21704     // defaults : white / black...
21705     this.applyBlacklists();
21706     
21707     
21708     
21709 };
21710
21711
21712 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21713
21714
21715      /**
21716      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21717      */
21718     
21719     owner : false,
21720     
21721      /**
21722      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21723      *                        Roo.resizable.
21724      */
21725     resizable : false,
21726      /**
21727      * @cfg {Number} height (in pixels)
21728      */   
21729     height: 300,
21730    /**
21731      * @cfg {Number} width (in pixels)
21732      */   
21733     width: 500,
21734     
21735     /**
21736      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21737      * 
21738      */
21739     stylesheets: false,
21740     
21741     // id of frame..
21742     frameId: false,
21743     
21744     // private properties
21745     validationEvent : false,
21746     deferHeight: true,
21747     initialized : false,
21748     activated : false,
21749     sourceEditMode : false,
21750     onFocus : Roo.emptyFn,
21751     iframePad:3,
21752     hideMode:'offsets',
21753     
21754     clearUp: true,
21755     
21756     // blacklist + whitelisted elements..
21757     black: false,
21758     white: false,
21759      
21760     bodyCls : '',
21761
21762     /**
21763      * Protected method that will not generally be called directly. It
21764      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21765      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21766      */
21767     getDocMarkup : function(){
21768         // body styles..
21769         var st = '';
21770         
21771         // inherit styels from page...?? 
21772         if (this.stylesheets === false) {
21773             
21774             Roo.get(document.head).select('style').each(function(node) {
21775                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21776             });
21777             
21778             Roo.get(document.head).select('link').each(function(node) { 
21779                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21780             });
21781             
21782         } else if (!this.stylesheets.length) {
21783                 // simple..
21784                 st = '<style type="text/css">' +
21785                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21786                    '</style>';
21787         } else { 
21788             st = '<style type="text/css">' +
21789                     this.stylesheets +
21790                 '</style>';
21791         }
21792         
21793         st +=  '<style type="text/css">' +
21794             'IMG { cursor: pointer } ' +
21795         '</style>';
21796
21797         var cls = 'roo-htmleditor-body';
21798         
21799         if(this.bodyCls.length){
21800             cls += ' ' + this.bodyCls;
21801         }
21802         
21803         return '<html><head>' + st  +
21804             //<style type="text/css">' +
21805             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21806             //'</style>' +
21807             ' </head><body class="' +  cls + '"></body></html>';
21808     },
21809
21810     // private
21811     onRender : function(ct, position)
21812     {
21813         var _t = this;
21814         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21815         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21816         
21817         
21818         this.el.dom.style.border = '0 none';
21819         this.el.dom.setAttribute('tabIndex', -1);
21820         this.el.addClass('x-hidden hide');
21821         
21822         
21823         
21824         if(Roo.isIE){ // fix IE 1px bogus margin
21825             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21826         }
21827        
21828         
21829         this.frameId = Roo.id();
21830         
21831          
21832         
21833         var iframe = this.owner.wrap.createChild({
21834             tag: 'iframe',
21835             cls: 'form-control', // bootstrap..
21836             id: this.frameId,
21837             name: this.frameId,
21838             frameBorder : 'no',
21839             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21840         }, this.el
21841         );
21842         
21843         
21844         this.iframe = iframe.dom;
21845
21846          this.assignDocWin();
21847         
21848         this.doc.designMode = 'on';
21849        
21850         this.doc.open();
21851         this.doc.write(this.getDocMarkup());
21852         this.doc.close();
21853
21854         
21855         var task = { // must defer to wait for browser to be ready
21856             run : function(){
21857                 //console.log("run task?" + this.doc.readyState);
21858                 this.assignDocWin();
21859                 if(this.doc.body || this.doc.readyState == 'complete'){
21860                     try {
21861                         this.doc.designMode="on";
21862                     } catch (e) {
21863                         return;
21864                     }
21865                     Roo.TaskMgr.stop(task);
21866                     this.initEditor.defer(10, this);
21867                 }
21868             },
21869             interval : 10,
21870             duration: 10000,
21871             scope: this
21872         };
21873         Roo.TaskMgr.start(task);
21874
21875     },
21876
21877     // private
21878     onResize : function(w, h)
21879     {
21880          Roo.log('resize: ' +w + ',' + h );
21881         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21882         if(!this.iframe){
21883             return;
21884         }
21885         if(typeof w == 'number'){
21886             
21887             this.iframe.style.width = w + 'px';
21888         }
21889         if(typeof h == 'number'){
21890             
21891             this.iframe.style.height = h + 'px';
21892             if(this.doc){
21893                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21894             }
21895         }
21896         
21897     },
21898
21899     /**
21900      * Toggles the editor between standard and source edit mode.
21901      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21902      */
21903     toggleSourceEdit : function(sourceEditMode){
21904         
21905         this.sourceEditMode = sourceEditMode === true;
21906         
21907         if(this.sourceEditMode){
21908  
21909             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21910             
21911         }else{
21912             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21913             //this.iframe.className = '';
21914             this.deferFocus();
21915         }
21916         //this.setSize(this.owner.wrap.getSize());
21917         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21918     },
21919
21920     
21921   
21922
21923     /**
21924      * Protected method that will not generally be called directly. If you need/want
21925      * custom HTML cleanup, this is the method you should override.
21926      * @param {String} html The HTML to be cleaned
21927      * return {String} The cleaned HTML
21928      */
21929     cleanHtml : function(html){
21930         html = String(html);
21931         if(html.length > 5){
21932             if(Roo.isSafari){ // strip safari nonsense
21933                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21934             }
21935         }
21936         if(html == '&nbsp;'){
21937             html = '';
21938         }
21939         return html;
21940     },
21941
21942     /**
21943      * HTML Editor -> Textarea
21944      * Protected method that will not generally be called directly. Syncs the contents
21945      * of the editor iframe with the textarea.
21946      */
21947     syncValue : function(){
21948         if(this.initialized){
21949             var bd = (this.doc.body || this.doc.documentElement);
21950             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21951             var html = bd.innerHTML;
21952             if(Roo.isSafari){
21953                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21954                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21955                 if(m && m[1]){
21956                     html = '<div style="'+m[0]+'">' + html + '</div>';
21957                 }
21958             }
21959             html = this.cleanHtml(html);
21960             // fix up the special chars.. normaly like back quotes in word...
21961             // however we do not want to do this with chinese..
21962             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21963                 var cc = b.charCodeAt();
21964                 if (
21965                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21966                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21967                     (cc >= 0xf900 && cc < 0xfb00 )
21968                 ) {
21969                         return b;
21970                 }
21971                 return "&#"+cc+";" 
21972             });
21973             if(this.owner.fireEvent('beforesync', this, html) !== false){
21974                 this.el.dom.value = html;
21975                 this.owner.fireEvent('sync', this, html);
21976             }
21977         }
21978     },
21979
21980     /**
21981      * Protected method that will not generally be called directly. Pushes the value of the textarea
21982      * into the iframe editor.
21983      */
21984     pushValue : function(){
21985         if(this.initialized){
21986             var v = this.el.dom.value.trim();
21987             
21988 //            if(v.length < 1){
21989 //                v = '&#160;';
21990 //            }
21991             
21992             if(this.owner.fireEvent('beforepush', this, v) !== false){
21993                 var d = (this.doc.body || this.doc.documentElement);
21994                 d.innerHTML = v;
21995                 this.cleanUpPaste();
21996                 this.el.dom.value = d.innerHTML;
21997                 this.owner.fireEvent('push', this, v);
21998             }
21999         }
22000     },
22001
22002     // private
22003     deferFocus : function(){
22004         this.focus.defer(10, this);
22005     },
22006
22007     // doc'ed in Field
22008     focus : function(){
22009         if(this.win && !this.sourceEditMode){
22010             this.win.focus();
22011         }else{
22012             this.el.focus();
22013         }
22014     },
22015     
22016     assignDocWin: function()
22017     {
22018         var iframe = this.iframe;
22019         
22020          if(Roo.isIE){
22021             this.doc = iframe.contentWindow.document;
22022             this.win = iframe.contentWindow;
22023         } else {
22024 //            if (!Roo.get(this.frameId)) {
22025 //                return;
22026 //            }
22027 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22028 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22029             
22030             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22031                 return;
22032             }
22033             
22034             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22035             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22036         }
22037     },
22038     
22039     // private
22040     initEditor : function(){
22041         //console.log("INIT EDITOR");
22042         this.assignDocWin();
22043         
22044         
22045         
22046         this.doc.designMode="on";
22047         this.doc.open();
22048         this.doc.write(this.getDocMarkup());
22049         this.doc.close();
22050         
22051         var dbody = (this.doc.body || this.doc.documentElement);
22052         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22053         // this copies styles from the containing element into thsi one..
22054         // not sure why we need all of this..
22055         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22056         
22057         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22058         //ss['background-attachment'] = 'fixed'; // w3c
22059         dbody.bgProperties = 'fixed'; // ie
22060         //Roo.DomHelper.applyStyles(dbody, ss);
22061         Roo.EventManager.on(this.doc, {
22062             //'mousedown': this.onEditorEvent,
22063             'mouseup': this.onEditorEvent,
22064             'dblclick': this.onEditorEvent,
22065             'click': this.onEditorEvent,
22066             'keyup': this.onEditorEvent,
22067             buffer:100,
22068             scope: this
22069         });
22070         if(Roo.isGecko){
22071             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22072         }
22073         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22074             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22075         }
22076         this.initialized = true;
22077
22078         this.owner.fireEvent('initialize', this);
22079         this.pushValue();
22080     },
22081
22082     // private
22083     onDestroy : function(){
22084         
22085         
22086         
22087         if(this.rendered){
22088             
22089             //for (var i =0; i < this.toolbars.length;i++) {
22090             //    // fixme - ask toolbars for heights?
22091             //    this.toolbars[i].onDestroy();
22092            // }
22093             
22094             //this.wrap.dom.innerHTML = '';
22095             //this.wrap.remove();
22096         }
22097     },
22098
22099     // private
22100     onFirstFocus : function(){
22101         
22102         this.assignDocWin();
22103         
22104         
22105         this.activated = true;
22106          
22107     
22108         if(Roo.isGecko){ // prevent silly gecko errors
22109             this.win.focus();
22110             var s = this.win.getSelection();
22111             if(!s.focusNode || s.focusNode.nodeType != 3){
22112                 var r = s.getRangeAt(0);
22113                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22114                 r.collapse(true);
22115                 this.deferFocus();
22116             }
22117             try{
22118                 this.execCmd('useCSS', true);
22119                 this.execCmd('styleWithCSS', false);
22120             }catch(e){}
22121         }
22122         this.owner.fireEvent('activate', this);
22123     },
22124
22125     // private
22126     adjustFont: function(btn){
22127         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22128         //if(Roo.isSafari){ // safari
22129         //    adjust *= 2;
22130        // }
22131         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22132         if(Roo.isSafari){ // safari
22133             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22134             v =  (v < 10) ? 10 : v;
22135             v =  (v > 48) ? 48 : v;
22136             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22137             
22138         }
22139         
22140         
22141         v = Math.max(1, v+adjust);
22142         
22143         this.execCmd('FontSize', v  );
22144     },
22145
22146     onEditorEvent : function(e)
22147     {
22148         this.owner.fireEvent('editorevent', this, e);
22149       //  this.updateToolbar();
22150         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22151     },
22152
22153     insertTag : function(tg)
22154     {
22155         // could be a bit smarter... -> wrap the current selected tRoo..
22156         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22157             
22158             range = this.createRange(this.getSelection());
22159             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22160             wrappingNode.appendChild(range.extractContents());
22161             range.insertNode(wrappingNode);
22162
22163             return;
22164             
22165             
22166             
22167         }
22168         this.execCmd("formatblock",   tg);
22169         
22170     },
22171     
22172     insertText : function(txt)
22173     {
22174         
22175         
22176         var range = this.createRange();
22177         range.deleteContents();
22178                //alert(Sender.getAttribute('label'));
22179                
22180         range.insertNode(this.doc.createTextNode(txt));
22181     } ,
22182     
22183      
22184
22185     /**
22186      * Executes a Midas editor command on the editor document and performs necessary focus and
22187      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22188      * @param {String} cmd The Midas command
22189      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22190      */
22191     relayCmd : function(cmd, value){
22192         this.win.focus();
22193         this.execCmd(cmd, value);
22194         this.owner.fireEvent('editorevent', this);
22195         //this.updateToolbar();
22196         this.owner.deferFocus();
22197     },
22198
22199     /**
22200      * Executes a Midas editor command directly on the editor document.
22201      * For visual commands, you should use {@link #relayCmd} instead.
22202      * <b>This should only be called after the editor is initialized.</b>
22203      * @param {String} cmd The Midas command
22204      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22205      */
22206     execCmd : function(cmd, value){
22207         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22208         this.syncValue();
22209     },
22210  
22211  
22212    
22213     /**
22214      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22215      * to insert tRoo.
22216      * @param {String} text | dom node.. 
22217      */
22218     insertAtCursor : function(text)
22219     {
22220         
22221         if(!this.activated){
22222             return;
22223         }
22224         /*
22225         if(Roo.isIE){
22226             this.win.focus();
22227             var r = this.doc.selection.createRange();
22228             if(r){
22229                 r.collapse(true);
22230                 r.pasteHTML(text);
22231                 this.syncValue();
22232                 this.deferFocus();
22233             
22234             }
22235             return;
22236         }
22237         */
22238         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22239             this.win.focus();
22240             
22241             
22242             // from jquery ui (MIT licenced)
22243             var range, node;
22244             var win = this.win;
22245             
22246             if (win.getSelection && win.getSelection().getRangeAt) {
22247                 range = win.getSelection().getRangeAt(0);
22248                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22249                 range.insertNode(node);
22250             } else if (win.document.selection && win.document.selection.createRange) {
22251                 // no firefox support
22252                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22253                 win.document.selection.createRange().pasteHTML(txt);
22254             } else {
22255                 // no firefox support
22256                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22257                 this.execCmd('InsertHTML', txt);
22258             } 
22259             
22260             this.syncValue();
22261             
22262             this.deferFocus();
22263         }
22264     },
22265  // private
22266     mozKeyPress : function(e){
22267         if(e.ctrlKey){
22268             var c = e.getCharCode(), cmd;
22269           
22270             if(c > 0){
22271                 c = String.fromCharCode(c).toLowerCase();
22272                 switch(c){
22273                     case 'b':
22274                         cmd = 'bold';
22275                         break;
22276                     case 'i':
22277                         cmd = 'italic';
22278                         break;
22279                     
22280                     case 'u':
22281                         cmd = 'underline';
22282                         break;
22283                     
22284                     case 'v':
22285                         this.cleanUpPaste.defer(100, this);
22286                         return;
22287                         
22288                 }
22289                 if(cmd){
22290                     this.win.focus();
22291                     this.execCmd(cmd);
22292                     this.deferFocus();
22293                     e.preventDefault();
22294                 }
22295                 
22296             }
22297         }
22298     },
22299
22300     // private
22301     fixKeys : function(){ // load time branching for fastest keydown performance
22302         if(Roo.isIE){
22303             return function(e){
22304                 var k = e.getKey(), r;
22305                 if(k == e.TAB){
22306                     e.stopEvent();
22307                     r = this.doc.selection.createRange();
22308                     if(r){
22309                         r.collapse(true);
22310                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22311                         this.deferFocus();
22312                     }
22313                     return;
22314                 }
22315                 
22316                 if(k == e.ENTER){
22317                     r = this.doc.selection.createRange();
22318                     if(r){
22319                         var target = r.parentElement();
22320                         if(!target || target.tagName.toLowerCase() != 'li'){
22321                             e.stopEvent();
22322                             r.pasteHTML('<br />');
22323                             r.collapse(false);
22324                             r.select();
22325                         }
22326                     }
22327                 }
22328                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22329                     this.cleanUpPaste.defer(100, this);
22330                     return;
22331                 }
22332                 
22333                 
22334             };
22335         }else if(Roo.isOpera){
22336             return function(e){
22337                 var k = e.getKey();
22338                 if(k == e.TAB){
22339                     e.stopEvent();
22340                     this.win.focus();
22341                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22342                     this.deferFocus();
22343                 }
22344                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22345                     this.cleanUpPaste.defer(100, this);
22346                     return;
22347                 }
22348                 
22349             };
22350         }else if(Roo.isSafari){
22351             return function(e){
22352                 var k = e.getKey();
22353                 
22354                 if(k == e.TAB){
22355                     e.stopEvent();
22356                     this.execCmd('InsertText','\t');
22357                     this.deferFocus();
22358                     return;
22359                 }
22360                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22361                     this.cleanUpPaste.defer(100, this);
22362                     return;
22363                 }
22364                 
22365              };
22366         }
22367     }(),
22368     
22369     getAllAncestors: function()
22370     {
22371         var p = this.getSelectedNode();
22372         var a = [];
22373         if (!p) {
22374             a.push(p); // push blank onto stack..
22375             p = this.getParentElement();
22376         }
22377         
22378         
22379         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22380             a.push(p);
22381             p = p.parentNode;
22382         }
22383         a.push(this.doc.body);
22384         return a;
22385     },
22386     lastSel : false,
22387     lastSelNode : false,
22388     
22389     
22390     getSelection : function() 
22391     {
22392         this.assignDocWin();
22393         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22394     },
22395     
22396     getSelectedNode: function() 
22397     {
22398         // this may only work on Gecko!!!
22399         
22400         // should we cache this!!!!
22401         
22402         
22403         
22404          
22405         var range = this.createRange(this.getSelection()).cloneRange();
22406         
22407         if (Roo.isIE) {
22408             var parent = range.parentElement();
22409             while (true) {
22410                 var testRange = range.duplicate();
22411                 testRange.moveToElementText(parent);
22412                 if (testRange.inRange(range)) {
22413                     break;
22414                 }
22415                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22416                     break;
22417                 }
22418                 parent = parent.parentElement;
22419             }
22420             return parent;
22421         }
22422         
22423         // is ancestor a text element.
22424         var ac =  range.commonAncestorContainer;
22425         if (ac.nodeType == 3) {
22426             ac = ac.parentNode;
22427         }
22428         
22429         var ar = ac.childNodes;
22430          
22431         var nodes = [];
22432         var other_nodes = [];
22433         var has_other_nodes = false;
22434         for (var i=0;i<ar.length;i++) {
22435             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22436                 continue;
22437             }
22438             // fullly contained node.
22439             
22440             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22441                 nodes.push(ar[i]);
22442                 continue;
22443             }
22444             
22445             // probably selected..
22446             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22447                 other_nodes.push(ar[i]);
22448                 continue;
22449             }
22450             // outer..
22451             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22452                 continue;
22453             }
22454             
22455             
22456             has_other_nodes = true;
22457         }
22458         if (!nodes.length && other_nodes.length) {
22459             nodes= other_nodes;
22460         }
22461         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22462             return false;
22463         }
22464         
22465         return nodes[0];
22466     },
22467     createRange: function(sel)
22468     {
22469         // this has strange effects when using with 
22470         // top toolbar - not sure if it's a great idea.
22471         //this.editor.contentWindow.focus();
22472         if (typeof sel != "undefined") {
22473             try {
22474                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22475             } catch(e) {
22476                 return this.doc.createRange();
22477             }
22478         } else {
22479             return this.doc.createRange();
22480         }
22481     },
22482     getParentElement: function()
22483     {
22484         
22485         this.assignDocWin();
22486         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22487         
22488         var range = this.createRange(sel);
22489          
22490         try {
22491             var p = range.commonAncestorContainer;
22492             while (p.nodeType == 3) { // text node
22493                 p = p.parentNode;
22494             }
22495             return p;
22496         } catch (e) {
22497             return null;
22498         }
22499     
22500     },
22501     /***
22502      *
22503      * Range intersection.. the hard stuff...
22504      *  '-1' = before
22505      *  '0' = hits..
22506      *  '1' = after.
22507      *         [ -- selected range --- ]
22508      *   [fail]                        [fail]
22509      *
22510      *    basically..
22511      *      if end is before start or  hits it. fail.
22512      *      if start is after end or hits it fail.
22513      *
22514      *   if either hits (but other is outside. - then it's not 
22515      *   
22516      *    
22517      **/
22518     
22519     
22520     // @see http://www.thismuchiknow.co.uk/?p=64.
22521     rangeIntersectsNode : function(range, node)
22522     {
22523         var nodeRange = node.ownerDocument.createRange();
22524         try {
22525             nodeRange.selectNode(node);
22526         } catch (e) {
22527             nodeRange.selectNodeContents(node);
22528         }
22529     
22530         var rangeStartRange = range.cloneRange();
22531         rangeStartRange.collapse(true);
22532     
22533         var rangeEndRange = range.cloneRange();
22534         rangeEndRange.collapse(false);
22535     
22536         var nodeStartRange = nodeRange.cloneRange();
22537         nodeStartRange.collapse(true);
22538     
22539         var nodeEndRange = nodeRange.cloneRange();
22540         nodeEndRange.collapse(false);
22541     
22542         return rangeStartRange.compareBoundaryPoints(
22543                  Range.START_TO_START, nodeEndRange) == -1 &&
22544                rangeEndRange.compareBoundaryPoints(
22545                  Range.START_TO_START, nodeStartRange) == 1;
22546         
22547          
22548     },
22549     rangeCompareNode : function(range, node)
22550     {
22551         var nodeRange = node.ownerDocument.createRange();
22552         try {
22553             nodeRange.selectNode(node);
22554         } catch (e) {
22555             nodeRange.selectNodeContents(node);
22556         }
22557         
22558         
22559         range.collapse(true);
22560     
22561         nodeRange.collapse(true);
22562      
22563         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22564         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22565          
22566         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22567         
22568         var nodeIsBefore   =  ss == 1;
22569         var nodeIsAfter    = ee == -1;
22570         
22571         if (nodeIsBefore && nodeIsAfter) {
22572             return 0; // outer
22573         }
22574         if (!nodeIsBefore && nodeIsAfter) {
22575             return 1; //right trailed.
22576         }
22577         
22578         if (nodeIsBefore && !nodeIsAfter) {
22579             return 2;  // left trailed.
22580         }
22581         // fully contined.
22582         return 3;
22583     },
22584
22585     // private? - in a new class?
22586     cleanUpPaste :  function()
22587     {
22588         // cleans up the whole document..
22589         Roo.log('cleanuppaste');
22590         
22591         this.cleanUpChildren(this.doc.body);
22592         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22593         if (clean != this.doc.body.innerHTML) {
22594             this.doc.body.innerHTML = clean;
22595         }
22596         
22597     },
22598     
22599     cleanWordChars : function(input) {// change the chars to hex code
22600         var he = Roo.HtmlEditorCore;
22601         
22602         var output = input;
22603         Roo.each(he.swapCodes, function(sw) { 
22604             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22605             
22606             output = output.replace(swapper, sw[1]);
22607         });
22608         
22609         return output;
22610     },
22611     
22612     
22613     cleanUpChildren : function (n)
22614     {
22615         if (!n.childNodes.length) {
22616             return;
22617         }
22618         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22619            this.cleanUpChild(n.childNodes[i]);
22620         }
22621     },
22622     
22623     
22624         
22625     
22626     cleanUpChild : function (node)
22627     {
22628         var ed = this;
22629         //console.log(node);
22630         if (node.nodeName == "#text") {
22631             // clean up silly Windows -- stuff?
22632             return; 
22633         }
22634         if (node.nodeName == "#comment") {
22635             node.parentNode.removeChild(node);
22636             // clean up silly Windows -- stuff?
22637             return; 
22638         }
22639         var lcname = node.tagName.toLowerCase();
22640         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22641         // whitelist of tags..
22642         
22643         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22644             // remove node.
22645             node.parentNode.removeChild(node);
22646             return;
22647             
22648         }
22649         
22650         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22651         
22652         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22653         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22654         
22655         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22656         //    remove_keep_children = true;
22657         //}
22658         
22659         if (remove_keep_children) {
22660             this.cleanUpChildren(node);
22661             // inserts everything just before this node...
22662             while (node.childNodes.length) {
22663                 var cn = node.childNodes[0];
22664                 node.removeChild(cn);
22665                 node.parentNode.insertBefore(cn, node);
22666             }
22667             node.parentNode.removeChild(node);
22668             return;
22669         }
22670         
22671         if (!node.attributes || !node.attributes.length) {
22672             this.cleanUpChildren(node);
22673             return;
22674         }
22675         
22676         function cleanAttr(n,v)
22677         {
22678             
22679             if (v.match(/^\./) || v.match(/^\//)) {
22680                 return;
22681             }
22682             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22683                 return;
22684             }
22685             if (v.match(/^#/)) {
22686                 return;
22687             }
22688 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22689             node.removeAttribute(n);
22690             
22691         }
22692         
22693         var cwhite = this.cwhite;
22694         var cblack = this.cblack;
22695             
22696         function cleanStyle(n,v)
22697         {
22698             if (v.match(/expression/)) { //XSS?? should we even bother..
22699                 node.removeAttribute(n);
22700                 return;
22701             }
22702             
22703             var parts = v.split(/;/);
22704             var clean = [];
22705             
22706             Roo.each(parts, function(p) {
22707                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22708                 if (!p.length) {
22709                     return true;
22710                 }
22711                 var l = p.split(':').shift().replace(/\s+/g,'');
22712                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22713                 
22714                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22715 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22716                     //node.removeAttribute(n);
22717                     return true;
22718                 }
22719                 //Roo.log()
22720                 // only allow 'c whitelisted system attributes'
22721                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22722 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22723                     //node.removeAttribute(n);
22724                     return true;
22725                 }
22726                 
22727                 
22728                  
22729                 
22730                 clean.push(p);
22731                 return true;
22732             });
22733             if (clean.length) { 
22734                 node.setAttribute(n, clean.join(';'));
22735             } else {
22736                 node.removeAttribute(n);
22737             }
22738             
22739         }
22740         
22741         
22742         for (var i = node.attributes.length-1; i > -1 ; i--) {
22743             var a = node.attributes[i];
22744             //console.log(a);
22745             
22746             if (a.name.toLowerCase().substr(0,2)=='on')  {
22747                 node.removeAttribute(a.name);
22748                 continue;
22749             }
22750             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22751                 node.removeAttribute(a.name);
22752                 continue;
22753             }
22754             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22755                 cleanAttr(a.name,a.value); // fixme..
22756                 continue;
22757             }
22758             if (a.name == 'style') {
22759                 cleanStyle(a.name,a.value);
22760                 continue;
22761             }
22762             /// clean up MS crap..
22763             // tecnically this should be a list of valid class'es..
22764             
22765             
22766             if (a.name == 'class') {
22767                 if (a.value.match(/^Mso/)) {
22768                     node.className = '';
22769                 }
22770                 
22771                 if (a.value.match(/^body$/)) {
22772                     node.className = '';
22773                 }
22774                 continue;
22775             }
22776             
22777             // style cleanup!?
22778             // class cleanup?
22779             
22780         }
22781         
22782         
22783         this.cleanUpChildren(node);
22784         
22785         
22786     },
22787     
22788     /**
22789      * Clean up MS wordisms...
22790      */
22791     cleanWord : function(node)
22792     {
22793         
22794         
22795         if (!node) {
22796             this.cleanWord(this.doc.body);
22797             return;
22798         }
22799         if (node.nodeName == "#text") {
22800             // clean up silly Windows -- stuff?
22801             return; 
22802         }
22803         if (node.nodeName == "#comment") {
22804             node.parentNode.removeChild(node);
22805             // clean up silly Windows -- stuff?
22806             return; 
22807         }
22808         
22809         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22810             node.parentNode.removeChild(node);
22811             return;
22812         }
22813         
22814         // remove - but keep children..
22815         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22816             while (node.childNodes.length) {
22817                 var cn = node.childNodes[0];
22818                 node.removeChild(cn);
22819                 node.parentNode.insertBefore(cn, node);
22820             }
22821             node.parentNode.removeChild(node);
22822             this.iterateChildren(node, this.cleanWord);
22823             return;
22824         }
22825         // clean styles
22826         if (node.className.length) {
22827             
22828             var cn = node.className.split(/\W+/);
22829             var cna = [];
22830             Roo.each(cn, function(cls) {
22831                 if (cls.match(/Mso[a-zA-Z]+/)) {
22832                     return;
22833                 }
22834                 cna.push(cls);
22835             });
22836             node.className = cna.length ? cna.join(' ') : '';
22837             if (!cna.length) {
22838                 node.removeAttribute("class");
22839             }
22840         }
22841         
22842         if (node.hasAttribute("lang")) {
22843             node.removeAttribute("lang");
22844         }
22845         
22846         if (node.hasAttribute("style")) {
22847             
22848             var styles = node.getAttribute("style").split(";");
22849             var nstyle = [];
22850             Roo.each(styles, function(s) {
22851                 if (!s.match(/:/)) {
22852                     return;
22853                 }
22854                 var kv = s.split(":");
22855                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22856                     return;
22857                 }
22858                 // what ever is left... we allow.
22859                 nstyle.push(s);
22860             });
22861             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22862             if (!nstyle.length) {
22863                 node.removeAttribute('style');
22864             }
22865         }
22866         this.iterateChildren(node, this.cleanWord);
22867         
22868         
22869         
22870     },
22871     /**
22872      * iterateChildren of a Node, calling fn each time, using this as the scole..
22873      * @param {DomNode} node node to iterate children of.
22874      * @param {Function} fn method of this class to call on each item.
22875      */
22876     iterateChildren : function(node, fn)
22877     {
22878         if (!node.childNodes.length) {
22879                 return;
22880         }
22881         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22882            fn.call(this, node.childNodes[i])
22883         }
22884     },
22885     
22886     
22887     /**
22888      * cleanTableWidths.
22889      *
22890      * Quite often pasting from word etc.. results in tables with column and widths.
22891      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22892      *
22893      */
22894     cleanTableWidths : function(node)
22895     {
22896          
22897          
22898         if (!node) {
22899             this.cleanTableWidths(this.doc.body);
22900             return;
22901         }
22902         
22903         // ignore list...
22904         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22905             return; 
22906         }
22907         Roo.log(node.tagName);
22908         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22909             this.iterateChildren(node, this.cleanTableWidths);
22910             return;
22911         }
22912         if (node.hasAttribute('width')) {
22913             node.removeAttribute('width');
22914         }
22915         
22916          
22917         if (node.hasAttribute("style")) {
22918             // pretty basic...
22919             
22920             var styles = node.getAttribute("style").split(";");
22921             var nstyle = [];
22922             Roo.each(styles, function(s) {
22923                 if (!s.match(/:/)) {
22924                     return;
22925                 }
22926                 var kv = s.split(":");
22927                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22928                     return;
22929                 }
22930                 // what ever is left... we allow.
22931                 nstyle.push(s);
22932             });
22933             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22934             if (!nstyle.length) {
22935                 node.removeAttribute('style');
22936             }
22937         }
22938         
22939         this.iterateChildren(node, this.cleanTableWidths);
22940         
22941         
22942     },
22943     
22944     
22945     
22946     
22947     domToHTML : function(currentElement, depth, nopadtext) {
22948         
22949         depth = depth || 0;
22950         nopadtext = nopadtext || false;
22951     
22952         if (!currentElement) {
22953             return this.domToHTML(this.doc.body);
22954         }
22955         
22956         //Roo.log(currentElement);
22957         var j;
22958         var allText = false;
22959         var nodeName = currentElement.nodeName;
22960         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22961         
22962         if  (nodeName == '#text') {
22963             
22964             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22965         }
22966         
22967         
22968         var ret = '';
22969         if (nodeName != 'BODY') {
22970              
22971             var i = 0;
22972             // Prints the node tagName, such as <A>, <IMG>, etc
22973             if (tagName) {
22974                 var attr = [];
22975                 for(i = 0; i < currentElement.attributes.length;i++) {
22976                     // quoting?
22977                     var aname = currentElement.attributes.item(i).name;
22978                     if (!currentElement.attributes.item(i).value.length) {
22979                         continue;
22980                     }
22981                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22982                 }
22983                 
22984                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22985             } 
22986             else {
22987                 
22988                 // eack
22989             }
22990         } else {
22991             tagName = false;
22992         }
22993         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22994             return ret;
22995         }
22996         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22997             nopadtext = true;
22998         }
22999         
23000         
23001         // Traverse the tree
23002         i = 0;
23003         var currentElementChild = currentElement.childNodes.item(i);
23004         var allText = true;
23005         var innerHTML  = '';
23006         lastnode = '';
23007         while (currentElementChild) {
23008             // Formatting code (indent the tree so it looks nice on the screen)
23009             var nopad = nopadtext;
23010             if (lastnode == 'SPAN') {
23011                 nopad  = true;
23012             }
23013             // text
23014             if  (currentElementChild.nodeName == '#text') {
23015                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23016                 toadd = nopadtext ? toadd : toadd.trim();
23017                 if (!nopad && toadd.length > 80) {
23018                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23019                 }
23020                 innerHTML  += toadd;
23021                 
23022                 i++;
23023                 currentElementChild = currentElement.childNodes.item(i);
23024                 lastNode = '';
23025                 continue;
23026             }
23027             allText = false;
23028             
23029             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23030                 
23031             // Recursively traverse the tree structure of the child node
23032             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23033             lastnode = currentElementChild.nodeName;
23034             i++;
23035             currentElementChild=currentElement.childNodes.item(i);
23036         }
23037         
23038         ret += innerHTML;
23039         
23040         if (!allText) {
23041                 // The remaining code is mostly for formatting the tree
23042             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23043         }
23044         
23045         
23046         if (tagName) {
23047             ret+= "</"+tagName+">";
23048         }
23049         return ret;
23050         
23051     },
23052         
23053     applyBlacklists : function()
23054     {
23055         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23056         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23057         
23058         this.white = [];
23059         this.black = [];
23060         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23061             if (b.indexOf(tag) > -1) {
23062                 return;
23063             }
23064             this.white.push(tag);
23065             
23066         }, this);
23067         
23068         Roo.each(w, function(tag) {
23069             if (b.indexOf(tag) > -1) {
23070                 return;
23071             }
23072             if (this.white.indexOf(tag) > -1) {
23073                 return;
23074             }
23075             this.white.push(tag);
23076             
23077         }, this);
23078         
23079         
23080         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23081             if (w.indexOf(tag) > -1) {
23082                 return;
23083             }
23084             this.black.push(tag);
23085             
23086         }, this);
23087         
23088         Roo.each(b, function(tag) {
23089             if (w.indexOf(tag) > -1) {
23090                 return;
23091             }
23092             if (this.black.indexOf(tag) > -1) {
23093                 return;
23094             }
23095             this.black.push(tag);
23096             
23097         }, this);
23098         
23099         
23100         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23101         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23102         
23103         this.cwhite = [];
23104         this.cblack = [];
23105         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23106             if (b.indexOf(tag) > -1) {
23107                 return;
23108             }
23109             this.cwhite.push(tag);
23110             
23111         }, this);
23112         
23113         Roo.each(w, function(tag) {
23114             if (b.indexOf(tag) > -1) {
23115                 return;
23116             }
23117             if (this.cwhite.indexOf(tag) > -1) {
23118                 return;
23119             }
23120             this.cwhite.push(tag);
23121             
23122         }, this);
23123         
23124         
23125         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23126             if (w.indexOf(tag) > -1) {
23127                 return;
23128             }
23129             this.cblack.push(tag);
23130             
23131         }, this);
23132         
23133         Roo.each(b, function(tag) {
23134             if (w.indexOf(tag) > -1) {
23135                 return;
23136             }
23137             if (this.cblack.indexOf(tag) > -1) {
23138                 return;
23139             }
23140             this.cblack.push(tag);
23141             
23142         }, this);
23143     },
23144     
23145     setStylesheets : function(stylesheets)
23146     {
23147         if(typeof(stylesheets) == 'string'){
23148             Roo.get(this.iframe.contentDocument.head).createChild({
23149                 tag : 'link',
23150                 rel : 'stylesheet',
23151                 type : 'text/css',
23152                 href : stylesheets
23153             });
23154             
23155             return;
23156         }
23157         var _this = this;
23158      
23159         Roo.each(stylesheets, function(s) {
23160             if(!s.length){
23161                 return;
23162             }
23163             
23164             Roo.get(_this.iframe.contentDocument.head).createChild({
23165                 tag : 'link',
23166                 rel : 'stylesheet',
23167                 type : 'text/css',
23168                 href : s
23169             });
23170         });
23171
23172         
23173     },
23174     
23175     removeStylesheets : function()
23176     {
23177         var _this = this;
23178         
23179         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23180             s.remove();
23181         });
23182     },
23183     
23184     setStyle : function(style)
23185     {
23186         Roo.get(this.iframe.contentDocument.head).createChild({
23187             tag : 'style',
23188             type : 'text/css',
23189             html : style
23190         });
23191
23192         return;
23193     }
23194     
23195     // hide stuff that is not compatible
23196     /**
23197      * @event blur
23198      * @hide
23199      */
23200     /**
23201      * @event change
23202      * @hide
23203      */
23204     /**
23205      * @event focus
23206      * @hide
23207      */
23208     /**
23209      * @event specialkey
23210      * @hide
23211      */
23212     /**
23213      * @cfg {String} fieldClass @hide
23214      */
23215     /**
23216      * @cfg {String} focusClass @hide
23217      */
23218     /**
23219      * @cfg {String} autoCreate @hide
23220      */
23221     /**
23222      * @cfg {String} inputType @hide
23223      */
23224     /**
23225      * @cfg {String} invalidClass @hide
23226      */
23227     /**
23228      * @cfg {String} invalidText @hide
23229      */
23230     /**
23231      * @cfg {String} msgFx @hide
23232      */
23233     /**
23234      * @cfg {String} validateOnBlur @hide
23235      */
23236 });
23237
23238 Roo.HtmlEditorCore.white = [
23239         'area', 'br', 'img', 'input', 'hr', 'wbr',
23240         
23241        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23242        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23243        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23244        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23245        'table',   'ul',         'xmp', 
23246        
23247        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23248       'thead',   'tr', 
23249      
23250       'dir', 'menu', 'ol', 'ul', 'dl',
23251        
23252       'embed',  'object'
23253 ];
23254
23255
23256 Roo.HtmlEditorCore.black = [
23257     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23258         'applet', // 
23259         'base',   'basefont', 'bgsound', 'blink',  'body', 
23260         'frame',  'frameset', 'head',    'html',   'ilayer', 
23261         'iframe', 'layer',  'link',     'meta',    'object',   
23262         'script', 'style' ,'title',  'xml' // clean later..
23263 ];
23264 Roo.HtmlEditorCore.clean = [
23265     'script', 'style', 'title', 'xml'
23266 ];
23267 Roo.HtmlEditorCore.remove = [
23268     'font'
23269 ];
23270 // attributes..
23271
23272 Roo.HtmlEditorCore.ablack = [
23273     'on'
23274 ];
23275     
23276 Roo.HtmlEditorCore.aclean = [ 
23277     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23278 ];
23279
23280 // protocols..
23281 Roo.HtmlEditorCore.pwhite= [
23282         'http',  'https',  'mailto'
23283 ];
23284
23285 // white listed style attributes.
23286 Roo.HtmlEditorCore.cwhite= [
23287       //  'text-align', /// default is to allow most things..
23288       
23289          
23290 //        'font-size'//??
23291 ];
23292
23293 // black listed style attributes.
23294 Roo.HtmlEditorCore.cblack= [
23295       //  'font-size' -- this can be set by the project 
23296 ];
23297
23298
23299 Roo.HtmlEditorCore.swapCodes   =[ 
23300     [    8211, "--" ], 
23301     [    8212, "--" ], 
23302     [    8216,  "'" ],  
23303     [    8217, "'" ],  
23304     [    8220, '"' ],  
23305     [    8221, '"' ],  
23306     [    8226, "*" ],  
23307     [    8230, "..." ]
23308 ]; 
23309
23310     /*
23311  * - LGPL
23312  *
23313  * HtmlEditor
23314  * 
23315  */
23316
23317 /**
23318  * @class Roo.bootstrap.HtmlEditor
23319  * @extends Roo.bootstrap.TextArea
23320  * Bootstrap HtmlEditor class
23321
23322  * @constructor
23323  * Create a new HtmlEditor
23324  * @param {Object} config The config object
23325  */
23326
23327 Roo.bootstrap.HtmlEditor = function(config){
23328     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23329     if (!this.toolbars) {
23330         this.toolbars = [];
23331     }
23332     
23333     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23334     this.addEvents({
23335             /**
23336              * @event initialize
23337              * Fires when the editor is fully initialized (including the iframe)
23338              * @param {HtmlEditor} this
23339              */
23340             initialize: true,
23341             /**
23342              * @event activate
23343              * Fires when the editor is first receives the focus. Any insertion must wait
23344              * until after this event.
23345              * @param {HtmlEditor} this
23346              */
23347             activate: true,
23348              /**
23349              * @event beforesync
23350              * Fires before the textarea is updated with content from the editor iframe. Return false
23351              * to cancel the sync.
23352              * @param {HtmlEditor} this
23353              * @param {String} html
23354              */
23355             beforesync: true,
23356              /**
23357              * @event beforepush
23358              * Fires before the iframe editor is updated with content from the textarea. Return false
23359              * to cancel the push.
23360              * @param {HtmlEditor} this
23361              * @param {String} html
23362              */
23363             beforepush: true,
23364              /**
23365              * @event sync
23366              * Fires when the textarea is updated with content from the editor iframe.
23367              * @param {HtmlEditor} this
23368              * @param {String} html
23369              */
23370             sync: true,
23371              /**
23372              * @event push
23373              * Fires when the iframe editor is updated with content from the textarea.
23374              * @param {HtmlEditor} this
23375              * @param {String} html
23376              */
23377             push: true,
23378              /**
23379              * @event editmodechange
23380              * Fires when the editor switches edit modes
23381              * @param {HtmlEditor} this
23382              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23383              */
23384             editmodechange: true,
23385             /**
23386              * @event editorevent
23387              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23388              * @param {HtmlEditor} this
23389              */
23390             editorevent: true,
23391             /**
23392              * @event firstfocus
23393              * Fires when on first focus - needed by toolbars..
23394              * @param {HtmlEditor} this
23395              */
23396             firstfocus: true,
23397             /**
23398              * @event autosave
23399              * Auto save the htmlEditor value as a file into Events
23400              * @param {HtmlEditor} this
23401              */
23402             autosave: true,
23403             /**
23404              * @event savedpreview
23405              * preview the saved version of htmlEditor
23406              * @param {HtmlEditor} this
23407              */
23408             savedpreview: true
23409         });
23410 };
23411
23412
23413 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23414     
23415     
23416       /**
23417      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23418      */
23419     toolbars : false,
23420     
23421      /**
23422     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23423     */
23424     btns : [],
23425    
23426      /**
23427      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23428      *                        Roo.resizable.
23429      */
23430     resizable : false,
23431      /**
23432      * @cfg {Number} height (in pixels)
23433      */   
23434     height: 300,
23435    /**
23436      * @cfg {Number} width (in pixels)
23437      */   
23438     width: false,
23439     
23440     /**
23441      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23442      * 
23443      */
23444     stylesheets: false,
23445     
23446     // id of frame..
23447     frameId: false,
23448     
23449     // private properties
23450     validationEvent : false,
23451     deferHeight: true,
23452     initialized : false,
23453     activated : false,
23454     
23455     onFocus : Roo.emptyFn,
23456     iframePad:3,
23457     hideMode:'offsets',
23458     
23459     tbContainer : false,
23460     
23461     bodyCls : '',
23462     
23463     toolbarContainer :function() {
23464         return this.wrap.select('.x-html-editor-tb',true).first();
23465     },
23466
23467     /**
23468      * Protected method that will not generally be called directly. It
23469      * is called when the editor creates its toolbar. Override this method if you need to
23470      * add custom toolbar buttons.
23471      * @param {HtmlEditor} editor
23472      */
23473     createToolbar : function(){
23474         Roo.log('renewing');
23475         Roo.log("create toolbars");
23476         
23477         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23478         this.toolbars[0].render(this.toolbarContainer());
23479         
23480         return;
23481         
23482 //        if (!editor.toolbars || !editor.toolbars.length) {
23483 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23484 //        }
23485 //        
23486 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23487 //            editor.toolbars[i] = Roo.factory(
23488 //                    typeof(editor.toolbars[i]) == 'string' ?
23489 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23490 //                Roo.bootstrap.HtmlEditor);
23491 //            editor.toolbars[i].init(editor);
23492 //        }
23493     },
23494
23495      
23496     // private
23497     onRender : function(ct, position)
23498     {
23499        // Roo.log("Call onRender: " + this.xtype);
23500         var _t = this;
23501         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23502       
23503         this.wrap = this.inputEl().wrap({
23504             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23505         });
23506         
23507         this.editorcore.onRender(ct, position);
23508          
23509         if (this.resizable) {
23510             this.resizeEl = new Roo.Resizable(this.wrap, {
23511                 pinned : true,
23512                 wrap: true,
23513                 dynamic : true,
23514                 minHeight : this.height,
23515                 height: this.height,
23516                 handles : this.resizable,
23517                 width: this.width,
23518                 listeners : {
23519                     resize : function(r, w, h) {
23520                         _t.onResize(w,h); // -something
23521                     }
23522                 }
23523             });
23524             
23525         }
23526         this.createToolbar(this);
23527        
23528         
23529         if(!this.width && this.resizable){
23530             this.setSize(this.wrap.getSize());
23531         }
23532         if (this.resizeEl) {
23533             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23534             // should trigger onReize..
23535         }
23536         
23537     },
23538
23539     // private
23540     onResize : function(w, h)
23541     {
23542         Roo.log('resize: ' +w + ',' + h );
23543         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23544         var ew = false;
23545         var eh = false;
23546         
23547         if(this.inputEl() ){
23548             if(typeof w == 'number'){
23549                 var aw = w - this.wrap.getFrameWidth('lr');
23550                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23551                 ew = aw;
23552             }
23553             if(typeof h == 'number'){
23554                  var tbh = -11;  // fixme it needs to tool bar size!
23555                 for (var i =0; i < this.toolbars.length;i++) {
23556                     // fixme - ask toolbars for heights?
23557                     tbh += this.toolbars[i].el.getHeight();
23558                     //if (this.toolbars[i].footer) {
23559                     //    tbh += this.toolbars[i].footer.el.getHeight();
23560                     //}
23561                 }
23562               
23563                 
23564                 
23565                 
23566                 
23567                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23568                 ah -= 5; // knock a few pixes off for look..
23569                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23570                 var eh = ah;
23571             }
23572         }
23573         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23574         this.editorcore.onResize(ew,eh);
23575         
23576     },
23577
23578     /**
23579      * Toggles the editor between standard and source edit mode.
23580      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23581      */
23582     toggleSourceEdit : function(sourceEditMode)
23583     {
23584         this.editorcore.toggleSourceEdit(sourceEditMode);
23585         
23586         if(this.editorcore.sourceEditMode){
23587             Roo.log('editor - showing textarea');
23588             
23589 //            Roo.log('in');
23590 //            Roo.log(this.syncValue());
23591             this.syncValue();
23592             this.inputEl().removeClass(['hide', 'x-hidden']);
23593             this.inputEl().dom.removeAttribute('tabIndex');
23594             this.inputEl().focus();
23595         }else{
23596             Roo.log('editor - hiding textarea');
23597 //            Roo.log('out')
23598 //            Roo.log(this.pushValue()); 
23599             this.pushValue();
23600             
23601             this.inputEl().addClass(['hide', 'x-hidden']);
23602             this.inputEl().dom.setAttribute('tabIndex', -1);
23603             //this.deferFocus();
23604         }
23605          
23606         if(this.resizable){
23607             this.setSize(this.wrap.getSize());
23608         }
23609         
23610         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23611     },
23612  
23613     // private (for BoxComponent)
23614     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23615
23616     // private (for BoxComponent)
23617     getResizeEl : function(){
23618         return this.wrap;
23619     },
23620
23621     // private (for BoxComponent)
23622     getPositionEl : function(){
23623         return this.wrap;
23624     },
23625
23626     // private
23627     initEvents : function(){
23628         this.originalValue = this.getValue();
23629     },
23630
23631 //    /**
23632 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23633 //     * @method
23634 //     */
23635 //    markInvalid : Roo.emptyFn,
23636 //    /**
23637 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23638 //     * @method
23639 //     */
23640 //    clearInvalid : Roo.emptyFn,
23641
23642     setValue : function(v){
23643         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23644         this.editorcore.pushValue();
23645     },
23646
23647      
23648     // private
23649     deferFocus : function(){
23650         this.focus.defer(10, this);
23651     },
23652
23653     // doc'ed in Field
23654     focus : function(){
23655         this.editorcore.focus();
23656         
23657     },
23658       
23659
23660     // private
23661     onDestroy : function(){
23662         
23663         
23664         
23665         if(this.rendered){
23666             
23667             for (var i =0; i < this.toolbars.length;i++) {
23668                 // fixme - ask toolbars for heights?
23669                 this.toolbars[i].onDestroy();
23670             }
23671             
23672             this.wrap.dom.innerHTML = '';
23673             this.wrap.remove();
23674         }
23675     },
23676
23677     // private
23678     onFirstFocus : function(){
23679         //Roo.log("onFirstFocus");
23680         this.editorcore.onFirstFocus();
23681          for (var i =0; i < this.toolbars.length;i++) {
23682             this.toolbars[i].onFirstFocus();
23683         }
23684         
23685     },
23686     
23687     // private
23688     syncValue : function()
23689     {   
23690         this.editorcore.syncValue();
23691     },
23692     
23693     pushValue : function()
23694     {   
23695         this.editorcore.pushValue();
23696     }
23697      
23698     
23699     // hide stuff that is not compatible
23700     /**
23701      * @event blur
23702      * @hide
23703      */
23704     /**
23705      * @event change
23706      * @hide
23707      */
23708     /**
23709      * @event focus
23710      * @hide
23711      */
23712     /**
23713      * @event specialkey
23714      * @hide
23715      */
23716     /**
23717      * @cfg {String} fieldClass @hide
23718      */
23719     /**
23720      * @cfg {String} focusClass @hide
23721      */
23722     /**
23723      * @cfg {String} autoCreate @hide
23724      */
23725     /**
23726      * @cfg {String} inputType @hide
23727      */
23728     /**
23729      * @cfg {String} invalidClass @hide
23730      */
23731     /**
23732      * @cfg {String} invalidText @hide
23733      */
23734     /**
23735      * @cfg {String} msgFx @hide
23736      */
23737     /**
23738      * @cfg {String} validateOnBlur @hide
23739      */
23740 });
23741  
23742     
23743    
23744    
23745    
23746       
23747 Roo.namespace('Roo.bootstrap.htmleditor');
23748 /**
23749  * @class Roo.bootstrap.HtmlEditorToolbar1
23750  * Basic Toolbar
23751  * 
23752  * Usage:
23753  *
23754  new Roo.bootstrap.HtmlEditor({
23755     ....
23756     toolbars : [
23757         new Roo.bootstrap.HtmlEditorToolbar1({
23758             disable : { fonts: 1 , format: 1, ..., ... , ...],
23759             btns : [ .... ]
23760         })
23761     }
23762      
23763  * 
23764  * @cfg {Object} disable List of elements to disable..
23765  * @cfg {Array} btns List of additional buttons.
23766  * 
23767  * 
23768  * NEEDS Extra CSS? 
23769  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23770  */
23771  
23772 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23773 {
23774     
23775     Roo.apply(this, config);
23776     
23777     // default disabled, based on 'good practice'..
23778     this.disable = this.disable || {};
23779     Roo.applyIf(this.disable, {
23780         fontSize : true,
23781         colors : true,
23782         specialElements : true
23783     });
23784     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23785     
23786     this.editor = config.editor;
23787     this.editorcore = config.editor.editorcore;
23788     
23789     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23790     
23791     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23792     // dont call parent... till later.
23793 }
23794 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23795      
23796     bar : true,
23797     
23798     editor : false,
23799     editorcore : false,
23800     
23801     
23802     formats : [
23803         "p" ,  
23804         "h1","h2","h3","h4","h5","h6", 
23805         "pre", "code", 
23806         "abbr", "acronym", "address", "cite", "samp", "var",
23807         'div','span'
23808     ],
23809     
23810     onRender : function(ct, position)
23811     {
23812        // Roo.log("Call onRender: " + this.xtype);
23813         
23814        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23815        Roo.log(this.el);
23816        this.el.dom.style.marginBottom = '0';
23817        var _this = this;
23818        var editorcore = this.editorcore;
23819        var editor= this.editor;
23820        
23821        var children = [];
23822        var btn = function(id,cmd , toggle, handler, html){
23823        
23824             var  event = toggle ? 'toggle' : 'click';
23825        
23826             var a = {
23827                 size : 'sm',
23828                 xtype: 'Button',
23829                 xns: Roo.bootstrap,
23830                 glyphicon : id,
23831                 cmd : id || cmd,
23832                 enableToggle:toggle !== false,
23833                 html : html || '',
23834                 pressed : toggle ? false : null,
23835                 listeners : {}
23836             };
23837             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23838                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23839             };
23840             children.push(a);
23841             return a;
23842        }
23843        
23844     //    var cb_box = function...
23845         
23846         var style = {
23847                 xtype: 'Button',
23848                 size : 'sm',
23849                 xns: Roo.bootstrap,
23850                 glyphicon : 'font',
23851                 //html : 'submit'
23852                 menu : {
23853                     xtype: 'Menu',
23854                     xns: Roo.bootstrap,
23855                     items:  []
23856                 }
23857         };
23858         Roo.each(this.formats, function(f) {
23859             style.menu.items.push({
23860                 xtype :'MenuItem',
23861                 xns: Roo.bootstrap,
23862                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23863                 tagname : f,
23864                 listeners : {
23865                     click : function()
23866                     {
23867                         editorcore.insertTag(this.tagname);
23868                         editor.focus();
23869                     }
23870                 }
23871                 
23872             });
23873         });
23874         children.push(style);   
23875         
23876         btn('bold',false,true);
23877         btn('italic',false,true);
23878         btn('align-left', 'justifyleft',true);
23879         btn('align-center', 'justifycenter',true);
23880         btn('align-right' , 'justifyright',true);
23881         btn('link', false, false, function(btn) {
23882             //Roo.log("create link?");
23883             var url = prompt(this.createLinkText, this.defaultLinkValue);
23884             if(url && url != 'http:/'+'/'){
23885                 this.editorcore.relayCmd('createlink', url);
23886             }
23887         }),
23888         btn('list','insertunorderedlist',true);
23889         btn('pencil', false,true, function(btn){
23890                 Roo.log(this);
23891                 this.toggleSourceEdit(btn.pressed);
23892         });
23893         
23894         if (this.editor.btns.length > 0) {
23895             for (var i = 0; i<this.editor.btns.length; i++) {
23896                 children.push(this.editor.btns[i]);
23897             }
23898         }
23899         
23900         /*
23901         var cog = {
23902                 xtype: 'Button',
23903                 size : 'sm',
23904                 xns: Roo.bootstrap,
23905                 glyphicon : 'cog',
23906                 //html : 'submit'
23907                 menu : {
23908                     xtype: 'Menu',
23909                     xns: Roo.bootstrap,
23910                     items:  []
23911                 }
23912         };
23913         
23914         cog.menu.items.push({
23915             xtype :'MenuItem',
23916             xns: Roo.bootstrap,
23917             html : Clean styles,
23918             tagname : f,
23919             listeners : {
23920                 click : function()
23921                 {
23922                     editorcore.insertTag(this.tagname);
23923                     editor.focus();
23924                 }
23925             }
23926             
23927         });
23928        */
23929         
23930          
23931        this.xtype = 'NavSimplebar';
23932         
23933         for(var i=0;i< children.length;i++) {
23934             
23935             this.buttons.add(this.addxtypeChild(children[i]));
23936             
23937         }
23938         
23939         editor.on('editorevent', this.updateToolbar, this);
23940     },
23941     onBtnClick : function(id)
23942     {
23943        this.editorcore.relayCmd(id);
23944        this.editorcore.focus();
23945     },
23946     
23947     /**
23948      * Protected method that will not generally be called directly. It triggers
23949      * a toolbar update by reading the markup state of the current selection in the editor.
23950      */
23951     updateToolbar: function(){
23952
23953         if(!this.editorcore.activated){
23954             this.editor.onFirstFocus(); // is this neeed?
23955             return;
23956         }
23957
23958         var btns = this.buttons; 
23959         var doc = this.editorcore.doc;
23960         btns.get('bold').setActive(doc.queryCommandState('bold'));
23961         btns.get('italic').setActive(doc.queryCommandState('italic'));
23962         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23963         
23964         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23965         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23966         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23967         
23968         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23969         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23970          /*
23971         
23972         var ans = this.editorcore.getAllAncestors();
23973         if (this.formatCombo) {
23974             
23975             
23976             var store = this.formatCombo.store;
23977             this.formatCombo.setValue("");
23978             for (var i =0; i < ans.length;i++) {
23979                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23980                     // select it..
23981                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23982                     break;
23983                 }
23984             }
23985         }
23986         
23987         
23988         
23989         // hides menus... - so this cant be on a menu...
23990         Roo.bootstrap.MenuMgr.hideAll();
23991         */
23992         Roo.bootstrap.MenuMgr.hideAll();
23993         //this.editorsyncValue();
23994     },
23995     onFirstFocus: function() {
23996         this.buttons.each(function(item){
23997            item.enable();
23998         });
23999     },
24000     toggleSourceEdit : function(sourceEditMode){
24001         
24002           
24003         if(sourceEditMode){
24004             Roo.log("disabling buttons");
24005            this.buttons.each( function(item){
24006                 if(item.cmd != 'pencil'){
24007                     item.disable();
24008                 }
24009             });
24010           
24011         }else{
24012             Roo.log("enabling buttons");
24013             if(this.editorcore.initialized){
24014                 this.buttons.each( function(item){
24015                     item.enable();
24016                 });
24017             }
24018             
24019         }
24020         Roo.log("calling toggole on editor");
24021         // tell the editor that it's been pressed..
24022         this.editor.toggleSourceEdit(sourceEditMode);
24023        
24024     }
24025 });
24026
24027
24028
24029
24030
24031 /**
24032  * @class Roo.bootstrap.Table.AbstractSelectionModel
24033  * @extends Roo.util.Observable
24034  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24035  * implemented by descendant classes.  This class should not be directly instantiated.
24036  * @constructor
24037  */
24038 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24039     this.locked = false;
24040     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24041 };
24042
24043
24044 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24045     /** @ignore Called by the grid automatically. Do not call directly. */
24046     init : function(grid){
24047         this.grid = grid;
24048         this.initEvents();
24049     },
24050
24051     /**
24052      * Locks the selections.
24053      */
24054     lock : function(){
24055         this.locked = true;
24056     },
24057
24058     /**
24059      * Unlocks the selections.
24060      */
24061     unlock : function(){
24062         this.locked = false;
24063     },
24064
24065     /**
24066      * Returns true if the selections are locked.
24067      * @return {Boolean}
24068      */
24069     isLocked : function(){
24070         return this.locked;
24071     }
24072 });
24073 /**
24074  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24075  * @class Roo.bootstrap.Table.RowSelectionModel
24076  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24077  * It supports multiple selections and keyboard selection/navigation. 
24078  * @constructor
24079  * @param {Object} config
24080  */
24081
24082 Roo.bootstrap.Table.RowSelectionModel = function(config){
24083     Roo.apply(this, config);
24084     this.selections = new Roo.util.MixedCollection(false, function(o){
24085         return o.id;
24086     });
24087
24088     this.last = false;
24089     this.lastActive = false;
24090
24091     this.addEvents({
24092         /**
24093              * @event selectionchange
24094              * Fires when the selection changes
24095              * @param {SelectionModel} this
24096              */
24097             "selectionchange" : true,
24098         /**
24099              * @event afterselectionchange
24100              * Fires after the selection changes (eg. by key press or clicking)
24101              * @param {SelectionModel} this
24102              */
24103             "afterselectionchange" : true,
24104         /**
24105              * @event beforerowselect
24106              * Fires when a row is selected being selected, return false to cancel.
24107              * @param {SelectionModel} this
24108              * @param {Number} rowIndex The selected index
24109              * @param {Boolean} keepExisting False if other selections will be cleared
24110              */
24111             "beforerowselect" : true,
24112         /**
24113              * @event rowselect
24114              * Fires when a row is selected.
24115              * @param {SelectionModel} this
24116              * @param {Number} rowIndex The selected index
24117              * @param {Roo.data.Record} r The record
24118              */
24119             "rowselect" : true,
24120         /**
24121              * @event rowdeselect
24122              * Fires when a row is deselected.
24123              * @param {SelectionModel} this
24124              * @param {Number} rowIndex The selected index
24125              */
24126         "rowdeselect" : true
24127     });
24128     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24129     this.locked = false;
24130  };
24131
24132 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24133     /**
24134      * @cfg {Boolean} singleSelect
24135      * True to allow selection of only one row at a time (defaults to false)
24136      */
24137     singleSelect : false,
24138
24139     // private
24140     initEvents : function()
24141     {
24142
24143         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24144         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24145         //}else{ // allow click to work like normal
24146          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24147         //}
24148         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24149         this.grid.on("rowclick", this.handleMouseDown, this);
24150         
24151         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24152             "up" : function(e){
24153                 if(!e.shiftKey){
24154                     this.selectPrevious(e.shiftKey);
24155                 }else if(this.last !== false && this.lastActive !== false){
24156                     var last = this.last;
24157                     this.selectRange(this.last,  this.lastActive-1);
24158                     this.grid.getView().focusRow(this.lastActive);
24159                     if(last !== false){
24160                         this.last = last;
24161                     }
24162                 }else{
24163                     this.selectFirstRow();
24164                 }
24165                 this.fireEvent("afterselectionchange", this);
24166             },
24167             "down" : function(e){
24168                 if(!e.shiftKey){
24169                     this.selectNext(e.shiftKey);
24170                 }else if(this.last !== false && this.lastActive !== false){
24171                     var last = this.last;
24172                     this.selectRange(this.last,  this.lastActive+1);
24173                     this.grid.getView().focusRow(this.lastActive);
24174                     if(last !== false){
24175                         this.last = last;
24176                     }
24177                 }else{
24178                     this.selectFirstRow();
24179                 }
24180                 this.fireEvent("afterselectionchange", this);
24181             },
24182             scope: this
24183         });
24184         this.grid.store.on('load', function(){
24185             this.selections.clear();
24186         },this);
24187         /*
24188         var view = this.grid.view;
24189         view.on("refresh", this.onRefresh, this);
24190         view.on("rowupdated", this.onRowUpdated, this);
24191         view.on("rowremoved", this.onRemove, this);
24192         */
24193     },
24194
24195     // private
24196     onRefresh : function()
24197     {
24198         var ds = this.grid.store, i, v = this.grid.view;
24199         var s = this.selections;
24200         s.each(function(r){
24201             if((i = ds.indexOfId(r.id)) != -1){
24202                 v.onRowSelect(i);
24203             }else{
24204                 s.remove(r);
24205             }
24206         });
24207     },
24208
24209     // private
24210     onRemove : function(v, index, r){
24211         this.selections.remove(r);
24212     },
24213
24214     // private
24215     onRowUpdated : function(v, index, r){
24216         if(this.isSelected(r)){
24217             v.onRowSelect(index);
24218         }
24219     },
24220
24221     /**
24222      * Select records.
24223      * @param {Array} records The records to select
24224      * @param {Boolean} keepExisting (optional) True to keep existing selections
24225      */
24226     selectRecords : function(records, keepExisting)
24227     {
24228         if(!keepExisting){
24229             this.clearSelections();
24230         }
24231             var ds = this.grid.store;
24232         for(var i = 0, len = records.length; i < len; i++){
24233             this.selectRow(ds.indexOf(records[i]), true);
24234         }
24235     },
24236
24237     /**
24238      * Gets the number of selected rows.
24239      * @return {Number}
24240      */
24241     getCount : function(){
24242         return this.selections.length;
24243     },
24244
24245     /**
24246      * Selects the first row in the grid.
24247      */
24248     selectFirstRow : function(){
24249         this.selectRow(0);
24250     },
24251
24252     /**
24253      * Select the last row.
24254      * @param {Boolean} keepExisting (optional) True to keep existing selections
24255      */
24256     selectLastRow : function(keepExisting){
24257         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24258         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24259     },
24260
24261     /**
24262      * Selects the row immediately following the last selected row.
24263      * @param {Boolean} keepExisting (optional) True to keep existing selections
24264      */
24265     selectNext : function(keepExisting)
24266     {
24267             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24268             this.selectRow(this.last+1, keepExisting);
24269             this.grid.getView().focusRow(this.last);
24270         }
24271     },
24272
24273     /**
24274      * Selects the row that precedes the last selected row.
24275      * @param {Boolean} keepExisting (optional) True to keep existing selections
24276      */
24277     selectPrevious : function(keepExisting){
24278         if(this.last){
24279             this.selectRow(this.last-1, keepExisting);
24280             this.grid.getView().focusRow(this.last);
24281         }
24282     },
24283
24284     /**
24285      * Returns the selected records
24286      * @return {Array} Array of selected records
24287      */
24288     getSelections : function(){
24289         return [].concat(this.selections.items);
24290     },
24291
24292     /**
24293      * Returns the first selected record.
24294      * @return {Record}
24295      */
24296     getSelected : function(){
24297         return this.selections.itemAt(0);
24298     },
24299
24300
24301     /**
24302      * Clears all selections.
24303      */
24304     clearSelections : function(fast)
24305     {
24306         if(this.locked) {
24307             return;
24308         }
24309         if(fast !== true){
24310                 var ds = this.grid.store;
24311             var s = this.selections;
24312             s.each(function(r){
24313                 this.deselectRow(ds.indexOfId(r.id));
24314             }, this);
24315             s.clear();
24316         }else{
24317             this.selections.clear();
24318         }
24319         this.last = false;
24320     },
24321
24322
24323     /**
24324      * Selects all rows.
24325      */
24326     selectAll : function(){
24327         if(this.locked) {
24328             return;
24329         }
24330         this.selections.clear();
24331         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24332             this.selectRow(i, true);
24333         }
24334     },
24335
24336     /**
24337      * Returns True if there is a selection.
24338      * @return {Boolean}
24339      */
24340     hasSelection : function(){
24341         return this.selections.length > 0;
24342     },
24343
24344     /**
24345      * Returns True if the specified row is selected.
24346      * @param {Number/Record} record The record or index of the record to check
24347      * @return {Boolean}
24348      */
24349     isSelected : function(index){
24350             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24351         return (r && this.selections.key(r.id) ? true : false);
24352     },
24353
24354     /**
24355      * Returns True if the specified record id is selected.
24356      * @param {String} id The id of record to check
24357      * @return {Boolean}
24358      */
24359     isIdSelected : function(id){
24360         return (this.selections.key(id) ? true : false);
24361     },
24362
24363
24364     // private
24365     handleMouseDBClick : function(e, t){
24366         
24367     },
24368     // private
24369     handleMouseDown : function(e, t)
24370     {
24371             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24372         if(this.isLocked() || rowIndex < 0 ){
24373             return;
24374         };
24375         if(e.shiftKey && this.last !== false){
24376             var last = this.last;
24377             this.selectRange(last, rowIndex, e.ctrlKey);
24378             this.last = last; // reset the last
24379             t.focus();
24380     
24381         }else{
24382             var isSelected = this.isSelected(rowIndex);
24383             //Roo.log("select row:" + rowIndex);
24384             if(isSelected){
24385                 this.deselectRow(rowIndex);
24386             } else {
24387                         this.selectRow(rowIndex, true);
24388             }
24389     
24390             /*
24391                 if(e.button !== 0 && isSelected){
24392                 alert('rowIndex 2: ' + rowIndex);
24393                     view.focusRow(rowIndex);
24394                 }else if(e.ctrlKey && isSelected){
24395                     this.deselectRow(rowIndex);
24396                 }else if(!isSelected){
24397                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24398                     view.focusRow(rowIndex);
24399                 }
24400             */
24401         }
24402         this.fireEvent("afterselectionchange", this);
24403     },
24404     // private
24405     handleDragableRowClick :  function(grid, rowIndex, e) 
24406     {
24407         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24408             this.selectRow(rowIndex, false);
24409             grid.view.focusRow(rowIndex);
24410              this.fireEvent("afterselectionchange", this);
24411         }
24412     },
24413     
24414     /**
24415      * Selects multiple rows.
24416      * @param {Array} rows Array of the indexes of the row to select
24417      * @param {Boolean} keepExisting (optional) True to keep existing selections
24418      */
24419     selectRows : function(rows, keepExisting){
24420         if(!keepExisting){
24421             this.clearSelections();
24422         }
24423         for(var i = 0, len = rows.length; i < len; i++){
24424             this.selectRow(rows[i], true);
24425         }
24426     },
24427
24428     /**
24429      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24430      * @param {Number} startRow The index of the first row in the range
24431      * @param {Number} endRow The index of the last row in the range
24432      * @param {Boolean} keepExisting (optional) True to retain existing selections
24433      */
24434     selectRange : function(startRow, endRow, keepExisting){
24435         if(this.locked) {
24436             return;
24437         }
24438         if(!keepExisting){
24439             this.clearSelections();
24440         }
24441         if(startRow <= endRow){
24442             for(var i = startRow; i <= endRow; i++){
24443                 this.selectRow(i, true);
24444             }
24445         }else{
24446             for(var i = startRow; i >= endRow; i--){
24447                 this.selectRow(i, true);
24448             }
24449         }
24450     },
24451
24452     /**
24453      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24454      * @param {Number} startRow The index of the first row in the range
24455      * @param {Number} endRow The index of the last row in the range
24456      */
24457     deselectRange : function(startRow, endRow, preventViewNotify){
24458         if(this.locked) {
24459             return;
24460         }
24461         for(var i = startRow; i <= endRow; i++){
24462             this.deselectRow(i, preventViewNotify);
24463         }
24464     },
24465
24466     /**
24467      * Selects a row.
24468      * @param {Number} row The index of the row to select
24469      * @param {Boolean} keepExisting (optional) True to keep existing selections
24470      */
24471     selectRow : function(index, keepExisting, preventViewNotify)
24472     {
24473             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24474             return;
24475         }
24476         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24477             if(!keepExisting || this.singleSelect){
24478                 this.clearSelections();
24479             }
24480             
24481             var r = this.grid.store.getAt(index);
24482             //console.log('selectRow - record id :' + r.id);
24483             
24484             this.selections.add(r);
24485             this.last = this.lastActive = index;
24486             if(!preventViewNotify){
24487                 var proxy = new Roo.Element(
24488                                 this.grid.getRowDom(index)
24489                 );
24490                 proxy.addClass('bg-info info');
24491             }
24492             this.fireEvent("rowselect", this, index, r);
24493             this.fireEvent("selectionchange", this);
24494         }
24495     },
24496
24497     /**
24498      * Deselects a row.
24499      * @param {Number} row The index of the row to deselect
24500      */
24501     deselectRow : function(index, preventViewNotify)
24502     {
24503         if(this.locked) {
24504             return;
24505         }
24506         if(this.last == index){
24507             this.last = false;
24508         }
24509         if(this.lastActive == index){
24510             this.lastActive = false;
24511         }
24512         
24513         var r = this.grid.store.getAt(index);
24514         if (!r) {
24515             return;
24516         }
24517         
24518         this.selections.remove(r);
24519         //.console.log('deselectRow - record id :' + r.id);
24520         if(!preventViewNotify){
24521         
24522             var proxy = new Roo.Element(
24523                 this.grid.getRowDom(index)
24524             );
24525             proxy.removeClass('bg-info info');
24526         }
24527         this.fireEvent("rowdeselect", this, index);
24528         this.fireEvent("selectionchange", this);
24529     },
24530
24531     // private
24532     restoreLast : function(){
24533         if(this._last){
24534             this.last = this._last;
24535         }
24536     },
24537
24538     // private
24539     acceptsNav : function(row, col, cm){
24540         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24541     },
24542
24543     // private
24544     onEditorKey : function(field, e){
24545         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24546         if(k == e.TAB){
24547             e.stopEvent();
24548             ed.completeEdit();
24549             if(e.shiftKey){
24550                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24551             }else{
24552                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24553             }
24554         }else if(k == e.ENTER && !e.ctrlKey){
24555             e.stopEvent();
24556             ed.completeEdit();
24557             if(e.shiftKey){
24558                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24559             }else{
24560                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24561             }
24562         }else if(k == e.ESC){
24563             ed.cancelEdit();
24564         }
24565         if(newCell){
24566             g.startEditing(newCell[0], newCell[1]);
24567         }
24568     }
24569 });
24570 /*
24571  * Based on:
24572  * Ext JS Library 1.1.1
24573  * Copyright(c) 2006-2007, Ext JS, LLC.
24574  *
24575  * Originally Released Under LGPL - original licence link has changed is not relivant.
24576  *
24577  * Fork - LGPL
24578  * <script type="text/javascript">
24579  */
24580  
24581 /**
24582  * @class Roo.bootstrap.PagingToolbar
24583  * @extends Roo.bootstrap.NavSimplebar
24584  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24585  * @constructor
24586  * Create a new PagingToolbar
24587  * @param {Object} config The config object
24588  * @param {Roo.data.Store} store
24589  */
24590 Roo.bootstrap.PagingToolbar = function(config)
24591 {
24592     // old args format still supported... - xtype is prefered..
24593         // created from xtype...
24594     
24595     this.ds = config.dataSource;
24596     
24597     if (config.store && !this.ds) {
24598         this.store= Roo.factory(config.store, Roo.data);
24599         this.ds = this.store;
24600         this.ds.xmodule = this.xmodule || false;
24601     }
24602     
24603     this.toolbarItems = [];
24604     if (config.items) {
24605         this.toolbarItems = config.items;
24606     }
24607     
24608     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24609     
24610     this.cursor = 0;
24611     
24612     if (this.ds) { 
24613         this.bind(this.ds);
24614     }
24615     
24616     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24617     
24618 };
24619
24620 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24621     /**
24622      * @cfg {Roo.data.Store} dataSource
24623      * The underlying data store providing the paged data
24624      */
24625     /**
24626      * @cfg {String/HTMLElement/Element} container
24627      * container The id or element that will contain the toolbar
24628      */
24629     /**
24630      * @cfg {Boolean} displayInfo
24631      * True to display the displayMsg (defaults to false)
24632      */
24633     /**
24634      * @cfg {Number} pageSize
24635      * The number of records to display per page (defaults to 20)
24636      */
24637     pageSize: 20,
24638     /**
24639      * @cfg {String} displayMsg
24640      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24641      */
24642     displayMsg : 'Displaying {0} - {1} of {2}',
24643     /**
24644      * @cfg {String} emptyMsg
24645      * The message to display when no records are found (defaults to "No data to display")
24646      */
24647     emptyMsg : 'No data to display',
24648     /**
24649      * Customizable piece of the default paging text (defaults to "Page")
24650      * @type String
24651      */
24652     beforePageText : "Page",
24653     /**
24654      * Customizable piece of the default paging text (defaults to "of %0")
24655      * @type String
24656      */
24657     afterPageText : "of {0}",
24658     /**
24659      * Customizable piece of the default paging text (defaults to "First Page")
24660      * @type String
24661      */
24662     firstText : "First Page",
24663     /**
24664      * Customizable piece of the default paging text (defaults to "Previous Page")
24665      * @type String
24666      */
24667     prevText : "Previous Page",
24668     /**
24669      * Customizable piece of the default paging text (defaults to "Next Page")
24670      * @type String
24671      */
24672     nextText : "Next Page",
24673     /**
24674      * Customizable piece of the default paging text (defaults to "Last Page")
24675      * @type String
24676      */
24677     lastText : "Last Page",
24678     /**
24679      * Customizable piece of the default paging text (defaults to "Refresh")
24680      * @type String
24681      */
24682     refreshText : "Refresh",
24683
24684     buttons : false,
24685     // private
24686     onRender : function(ct, position) 
24687     {
24688         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24689         this.navgroup.parentId = this.id;
24690         this.navgroup.onRender(this.el, null);
24691         // add the buttons to the navgroup
24692         
24693         if(this.displayInfo){
24694             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24695             this.displayEl = this.el.select('.x-paging-info', true).first();
24696 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24697 //            this.displayEl = navel.el.select('span',true).first();
24698         }
24699         
24700         var _this = this;
24701         
24702         if(this.buttons){
24703             Roo.each(_this.buttons, function(e){ // this might need to use render????
24704                Roo.factory(e).render(_this.el);
24705             });
24706         }
24707             
24708         Roo.each(_this.toolbarItems, function(e) {
24709             _this.navgroup.addItem(e);
24710         });
24711         
24712         
24713         this.first = this.navgroup.addItem({
24714             tooltip: this.firstText,
24715             cls: "prev",
24716             icon : 'fa fa-backward',
24717             disabled: true,
24718             preventDefault: true,
24719             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24720         });
24721         
24722         this.prev =  this.navgroup.addItem({
24723             tooltip: this.prevText,
24724             cls: "prev",
24725             icon : 'fa fa-step-backward',
24726             disabled: true,
24727             preventDefault: true,
24728             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24729         });
24730     //this.addSeparator();
24731         
24732         
24733         var field = this.navgroup.addItem( {
24734             tagtype : 'span',
24735             cls : 'x-paging-position',
24736             
24737             html : this.beforePageText  +
24738                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24739                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24740          } ); //?? escaped?
24741         
24742         this.field = field.el.select('input', true).first();
24743         this.field.on("keydown", this.onPagingKeydown, this);
24744         this.field.on("focus", function(){this.dom.select();});
24745     
24746     
24747         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24748         //this.field.setHeight(18);
24749         //this.addSeparator();
24750         this.next = this.navgroup.addItem({
24751             tooltip: this.nextText,
24752             cls: "next",
24753             html : ' <i class="fa fa-step-forward">',
24754             disabled: true,
24755             preventDefault: true,
24756             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24757         });
24758         this.last = this.navgroup.addItem({
24759             tooltip: this.lastText,
24760             icon : 'fa fa-forward',
24761             cls: "next",
24762             disabled: true,
24763             preventDefault: true,
24764             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24765         });
24766     //this.addSeparator();
24767         this.loading = this.navgroup.addItem({
24768             tooltip: this.refreshText,
24769             icon: 'fa fa-refresh',
24770             preventDefault: true,
24771             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24772         });
24773         
24774     },
24775
24776     // private
24777     updateInfo : function(){
24778         if(this.displayEl){
24779             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24780             var msg = count == 0 ?
24781                 this.emptyMsg :
24782                 String.format(
24783                     this.displayMsg,
24784                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24785                 );
24786             this.displayEl.update(msg);
24787         }
24788     },
24789
24790     // private
24791     onLoad : function(ds, r, o)
24792     {
24793         this.cursor = o.params.start ? o.params.start : 0;
24794         
24795         var d = this.getPageData(),
24796             ap = d.activePage,
24797             ps = d.pages;
24798         
24799         
24800         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24801         this.field.dom.value = ap;
24802         this.first.setDisabled(ap == 1);
24803         this.prev.setDisabled(ap == 1);
24804         this.next.setDisabled(ap == ps);
24805         this.last.setDisabled(ap == ps);
24806         this.loading.enable();
24807         this.updateInfo();
24808     },
24809
24810     // private
24811     getPageData : function(){
24812         var total = this.ds.getTotalCount();
24813         return {
24814             total : total,
24815             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24816             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24817         };
24818     },
24819
24820     // private
24821     onLoadError : function(){
24822         this.loading.enable();
24823     },
24824
24825     // private
24826     onPagingKeydown : function(e){
24827         var k = e.getKey();
24828         var d = this.getPageData();
24829         if(k == e.RETURN){
24830             var v = this.field.dom.value, pageNum;
24831             if(!v || isNaN(pageNum = parseInt(v, 10))){
24832                 this.field.dom.value = d.activePage;
24833                 return;
24834             }
24835             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24836             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24837             e.stopEvent();
24838         }
24839         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))
24840         {
24841           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24842           this.field.dom.value = pageNum;
24843           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24844           e.stopEvent();
24845         }
24846         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24847         {
24848           var v = this.field.dom.value, pageNum; 
24849           var increment = (e.shiftKey) ? 10 : 1;
24850           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24851                 increment *= -1;
24852           }
24853           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24854             this.field.dom.value = d.activePage;
24855             return;
24856           }
24857           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24858           {
24859             this.field.dom.value = parseInt(v, 10) + increment;
24860             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24861             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24862           }
24863           e.stopEvent();
24864         }
24865     },
24866
24867     // private
24868     beforeLoad : function(){
24869         if(this.loading){
24870             this.loading.disable();
24871         }
24872     },
24873
24874     // private
24875     onClick : function(which){
24876         
24877         var ds = this.ds;
24878         if (!ds) {
24879             return;
24880         }
24881         
24882         switch(which){
24883             case "first":
24884                 ds.load({params:{start: 0, limit: this.pageSize}});
24885             break;
24886             case "prev":
24887                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24888             break;
24889             case "next":
24890                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24891             break;
24892             case "last":
24893                 var total = ds.getTotalCount();
24894                 var extra = total % this.pageSize;
24895                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24896                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24897             break;
24898             case "refresh":
24899                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24900             break;
24901         }
24902     },
24903
24904     /**
24905      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24906      * @param {Roo.data.Store} store The data store to unbind
24907      */
24908     unbind : function(ds){
24909         ds.un("beforeload", this.beforeLoad, this);
24910         ds.un("load", this.onLoad, this);
24911         ds.un("loadexception", this.onLoadError, this);
24912         ds.un("remove", this.updateInfo, this);
24913         ds.un("add", this.updateInfo, this);
24914         this.ds = undefined;
24915     },
24916
24917     /**
24918      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24919      * @param {Roo.data.Store} store The data store to bind
24920      */
24921     bind : function(ds){
24922         ds.on("beforeload", this.beforeLoad, this);
24923         ds.on("load", this.onLoad, this);
24924         ds.on("loadexception", this.onLoadError, this);
24925         ds.on("remove", this.updateInfo, this);
24926         ds.on("add", this.updateInfo, this);
24927         this.ds = ds;
24928     }
24929 });/*
24930  * - LGPL
24931  *
24932  * element
24933  * 
24934  */
24935
24936 /**
24937  * @class Roo.bootstrap.MessageBar
24938  * @extends Roo.bootstrap.Component
24939  * Bootstrap MessageBar class
24940  * @cfg {String} html contents of the MessageBar
24941  * @cfg {String} weight (info | success | warning | danger) default info
24942  * @cfg {String} beforeClass insert the bar before the given class
24943  * @cfg {Boolean} closable (true | false) default false
24944  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24945  * 
24946  * @constructor
24947  * Create a new Element
24948  * @param {Object} config The config object
24949  */
24950
24951 Roo.bootstrap.MessageBar = function(config){
24952     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24953 };
24954
24955 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24956     
24957     html: '',
24958     weight: 'info',
24959     closable: false,
24960     fixed: false,
24961     beforeClass: 'bootstrap-sticky-wrap',
24962     
24963     getAutoCreate : function(){
24964         
24965         var cfg = {
24966             tag: 'div',
24967             cls: 'alert alert-dismissable alert-' + this.weight,
24968             cn: [
24969                 {
24970                     tag: 'span',
24971                     cls: 'message',
24972                     html: this.html || ''
24973                 }
24974             ]
24975         };
24976         
24977         if(this.fixed){
24978             cfg.cls += ' alert-messages-fixed';
24979         }
24980         
24981         if(this.closable){
24982             cfg.cn.push({
24983                 tag: 'button',
24984                 cls: 'close',
24985                 html: 'x'
24986             });
24987         }
24988         
24989         return cfg;
24990     },
24991     
24992     onRender : function(ct, position)
24993     {
24994         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24995         
24996         if(!this.el){
24997             var cfg = Roo.apply({},  this.getAutoCreate());
24998             cfg.id = Roo.id();
24999             
25000             if (this.cls) {
25001                 cfg.cls += ' ' + this.cls;
25002             }
25003             if (this.style) {
25004                 cfg.style = this.style;
25005             }
25006             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25007             
25008             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25009         }
25010         
25011         this.el.select('>button.close').on('click', this.hide, this);
25012         
25013     },
25014     
25015     show : function()
25016     {
25017         if (!this.rendered) {
25018             this.render();
25019         }
25020         
25021         this.el.show();
25022         
25023         this.fireEvent('show', this);
25024         
25025     },
25026     
25027     hide : function()
25028     {
25029         if (!this.rendered) {
25030             this.render();
25031         }
25032         
25033         this.el.hide();
25034         
25035         this.fireEvent('hide', this);
25036     },
25037     
25038     update : function()
25039     {
25040 //        var e = this.el.dom.firstChild;
25041 //        
25042 //        if(this.closable){
25043 //            e = e.nextSibling;
25044 //        }
25045 //        
25046 //        e.data = this.html || '';
25047
25048         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25049     }
25050    
25051 });
25052
25053  
25054
25055      /*
25056  * - LGPL
25057  *
25058  * Graph
25059  * 
25060  */
25061
25062
25063 /**
25064  * @class Roo.bootstrap.Graph
25065  * @extends Roo.bootstrap.Component
25066  * Bootstrap Graph class
25067 > Prameters
25068  -sm {number} sm 4
25069  -md {number} md 5
25070  @cfg {String} graphtype  bar | vbar | pie
25071  @cfg {number} g_x coodinator | centre x (pie)
25072  @cfg {number} g_y coodinator | centre y (pie)
25073  @cfg {number} g_r radius (pie)
25074  @cfg {number} g_height height of the chart (respected by all elements in the set)
25075  @cfg {number} g_width width of the chart (respected by all elements in the set)
25076  @cfg {Object} title The title of the chart
25077     
25078  -{Array}  values
25079  -opts (object) options for the chart 
25080      o {
25081      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25082      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25083      o vgutter (number)
25084      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.
25085      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25086      o to
25087      o stretch (boolean)
25088      o }
25089  -opts (object) options for the pie
25090      o{
25091      o cut
25092      o startAngle (number)
25093      o endAngle (number)
25094      } 
25095  *
25096  * @constructor
25097  * Create a new Input
25098  * @param {Object} config The config object
25099  */
25100
25101 Roo.bootstrap.Graph = function(config){
25102     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25103     
25104     this.addEvents({
25105         // img events
25106         /**
25107          * @event click
25108          * The img click event for the img.
25109          * @param {Roo.EventObject} e
25110          */
25111         "click" : true
25112     });
25113 };
25114
25115 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25116     
25117     sm: 4,
25118     md: 5,
25119     graphtype: 'bar',
25120     g_height: 250,
25121     g_width: 400,
25122     g_x: 50,
25123     g_y: 50,
25124     g_r: 30,
25125     opts:{
25126         //g_colors: this.colors,
25127         g_type: 'soft',
25128         g_gutter: '20%'
25129
25130     },
25131     title : false,
25132
25133     getAutoCreate : function(){
25134         
25135         var cfg = {
25136             tag: 'div',
25137             html : null
25138         };
25139         
25140         
25141         return  cfg;
25142     },
25143
25144     onRender : function(ct,position){
25145         
25146         
25147         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25148         
25149         if (typeof(Raphael) == 'undefined') {
25150             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25151             return;
25152         }
25153         
25154         this.raphael = Raphael(this.el.dom);
25155         
25156                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25157                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25158                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25159                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25160                 /*
25161                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25162                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25163                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25164                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25165                 
25166                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25167                 r.barchart(330, 10, 300, 220, data1);
25168                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25169                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25170                 */
25171                 
25172                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25173                 // r.barchart(30, 30, 560, 250,  xdata, {
25174                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25175                 //     axis : "0 0 1 1",
25176                 //     axisxlabels :  xdata
25177                 //     //yvalues : cols,
25178                    
25179                 // });
25180 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25181 //        
25182 //        this.load(null,xdata,{
25183 //                axis : "0 0 1 1",
25184 //                axisxlabels :  xdata
25185 //                });
25186
25187     },
25188
25189     load : function(graphtype,xdata,opts)
25190     {
25191         this.raphael.clear();
25192         if(!graphtype) {
25193             graphtype = this.graphtype;
25194         }
25195         if(!opts){
25196             opts = this.opts;
25197         }
25198         var r = this.raphael,
25199             fin = function () {
25200                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25201             },
25202             fout = function () {
25203                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25204             },
25205             pfin = function() {
25206                 this.sector.stop();
25207                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25208
25209                 if (this.label) {
25210                     this.label[0].stop();
25211                     this.label[0].attr({ r: 7.5 });
25212                     this.label[1].attr({ "font-weight": 800 });
25213                 }
25214             },
25215             pfout = function() {
25216                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25217
25218                 if (this.label) {
25219                     this.label[0].animate({ r: 5 }, 500, "bounce");
25220                     this.label[1].attr({ "font-weight": 400 });
25221                 }
25222             };
25223
25224         switch(graphtype){
25225             case 'bar':
25226                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25227                 break;
25228             case 'hbar':
25229                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25230                 break;
25231             case 'pie':
25232 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25233 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25234 //            
25235                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25236                 
25237                 break;
25238
25239         }
25240         
25241         if(this.title){
25242             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25243         }
25244         
25245     },
25246     
25247     setTitle: function(o)
25248     {
25249         this.title = o;
25250     },
25251     
25252     initEvents: function() {
25253         
25254         if(!this.href){
25255             this.el.on('click', this.onClick, this);
25256         }
25257     },
25258     
25259     onClick : function(e)
25260     {
25261         Roo.log('img onclick');
25262         this.fireEvent('click', this, e);
25263     }
25264    
25265 });
25266
25267  
25268 /*
25269  * - LGPL
25270  *
25271  * numberBox
25272  * 
25273  */
25274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25275
25276 /**
25277  * @class Roo.bootstrap.dash.NumberBox
25278  * @extends Roo.bootstrap.Component
25279  * Bootstrap NumberBox class
25280  * @cfg {String} headline Box headline
25281  * @cfg {String} content Box content
25282  * @cfg {String} icon Box icon
25283  * @cfg {String} footer Footer text
25284  * @cfg {String} fhref Footer href
25285  * 
25286  * @constructor
25287  * Create a new NumberBox
25288  * @param {Object} config The config object
25289  */
25290
25291
25292 Roo.bootstrap.dash.NumberBox = function(config){
25293     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25294     
25295 };
25296
25297 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25298     
25299     headline : '',
25300     content : '',
25301     icon : '',
25302     footer : '',
25303     fhref : '',
25304     ficon : '',
25305     
25306     getAutoCreate : function(){
25307         
25308         var cfg = {
25309             tag : 'div',
25310             cls : 'small-box ',
25311             cn : [
25312                 {
25313                     tag : 'div',
25314                     cls : 'inner',
25315                     cn :[
25316                         {
25317                             tag : 'h3',
25318                             cls : 'roo-headline',
25319                             html : this.headline
25320                         },
25321                         {
25322                             tag : 'p',
25323                             cls : 'roo-content',
25324                             html : this.content
25325                         }
25326                     ]
25327                 }
25328             ]
25329         };
25330         
25331         if(this.icon){
25332             cfg.cn.push({
25333                 tag : 'div',
25334                 cls : 'icon',
25335                 cn :[
25336                     {
25337                         tag : 'i',
25338                         cls : 'ion ' + this.icon
25339                     }
25340                 ]
25341             });
25342         }
25343         
25344         if(this.footer){
25345             var footer = {
25346                 tag : 'a',
25347                 cls : 'small-box-footer',
25348                 href : this.fhref || '#',
25349                 html : this.footer
25350             };
25351             
25352             cfg.cn.push(footer);
25353             
25354         }
25355         
25356         return  cfg;
25357     },
25358
25359     onRender : function(ct,position){
25360         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25361
25362
25363        
25364                 
25365     },
25366
25367     setHeadline: function (value)
25368     {
25369         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25370     },
25371     
25372     setFooter: function (value, href)
25373     {
25374         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25375         
25376         if(href){
25377             this.el.select('a.small-box-footer',true).first().attr('href', href);
25378         }
25379         
25380     },
25381
25382     setContent: function (value)
25383     {
25384         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25385     },
25386
25387     initEvents: function() 
25388     {   
25389         
25390     }
25391     
25392 });
25393
25394  
25395 /*
25396  * - LGPL
25397  *
25398  * TabBox
25399  * 
25400  */
25401 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25402
25403 /**
25404  * @class Roo.bootstrap.dash.TabBox
25405  * @extends Roo.bootstrap.Component
25406  * Bootstrap TabBox class
25407  * @cfg {String} title Title of the TabBox
25408  * @cfg {String} icon Icon of the TabBox
25409  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25410  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25411  * 
25412  * @constructor
25413  * Create a new TabBox
25414  * @param {Object} config The config object
25415  */
25416
25417
25418 Roo.bootstrap.dash.TabBox = function(config){
25419     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25420     this.addEvents({
25421         // raw events
25422         /**
25423          * @event addpane
25424          * When a pane is added
25425          * @param {Roo.bootstrap.dash.TabPane} pane
25426          */
25427         "addpane" : true,
25428         /**
25429          * @event activatepane
25430          * When a pane is activated
25431          * @param {Roo.bootstrap.dash.TabPane} pane
25432          */
25433         "activatepane" : true
25434         
25435          
25436     });
25437     
25438     this.panes = [];
25439 };
25440
25441 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25442
25443     title : '',
25444     icon : false,
25445     showtabs : true,
25446     tabScrollable : false,
25447     
25448     getChildContainer : function()
25449     {
25450         return this.el.select('.tab-content', true).first();
25451     },
25452     
25453     getAutoCreate : function(){
25454         
25455         var header = {
25456             tag: 'li',
25457             cls: 'pull-left header',
25458             html: this.title,
25459             cn : []
25460         };
25461         
25462         if(this.icon){
25463             header.cn.push({
25464                 tag: 'i',
25465                 cls: 'fa ' + this.icon
25466             });
25467         }
25468         
25469         var h = {
25470             tag: 'ul',
25471             cls: 'nav nav-tabs pull-right',
25472             cn: [
25473                 header
25474             ]
25475         };
25476         
25477         if(this.tabScrollable){
25478             h = {
25479                 tag: 'div',
25480                 cls: 'tab-header',
25481                 cn: [
25482                     {
25483                         tag: 'ul',
25484                         cls: 'nav nav-tabs pull-right',
25485                         cn: [
25486                             header
25487                         ]
25488                     }
25489                 ]
25490             };
25491         }
25492         
25493         var cfg = {
25494             tag: 'div',
25495             cls: 'nav-tabs-custom',
25496             cn: [
25497                 h,
25498                 {
25499                     tag: 'div',
25500                     cls: 'tab-content no-padding',
25501                     cn: []
25502                 }
25503             ]
25504         };
25505
25506         return  cfg;
25507     },
25508     initEvents : function()
25509     {
25510         //Roo.log('add add pane handler');
25511         this.on('addpane', this.onAddPane, this);
25512     },
25513      /**
25514      * Updates the box title
25515      * @param {String} html to set the title to.
25516      */
25517     setTitle : function(value)
25518     {
25519         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25520     },
25521     onAddPane : function(pane)
25522     {
25523         this.panes.push(pane);
25524         //Roo.log('addpane');
25525         //Roo.log(pane);
25526         // tabs are rendere left to right..
25527         if(!this.showtabs){
25528             return;
25529         }
25530         
25531         var ctr = this.el.select('.nav-tabs', true).first();
25532          
25533          
25534         var existing = ctr.select('.nav-tab',true);
25535         var qty = existing.getCount();;
25536         
25537         
25538         var tab = ctr.createChild({
25539             tag : 'li',
25540             cls : 'nav-tab' + (qty ? '' : ' active'),
25541             cn : [
25542                 {
25543                     tag : 'a',
25544                     href:'#',
25545                     html : pane.title
25546                 }
25547             ]
25548         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25549         pane.tab = tab;
25550         
25551         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25552         if (!qty) {
25553             pane.el.addClass('active');
25554         }
25555         
25556                 
25557     },
25558     onTabClick : function(ev,un,ob,pane)
25559     {
25560         //Roo.log('tab - prev default');
25561         ev.preventDefault();
25562         
25563         
25564         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25565         pane.tab.addClass('active');
25566         //Roo.log(pane.title);
25567         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25568         // technically we should have a deactivate event.. but maybe add later.
25569         // and it should not de-activate the selected tab...
25570         this.fireEvent('activatepane', pane);
25571         pane.el.addClass('active');
25572         pane.fireEvent('activate');
25573         
25574         
25575     },
25576     
25577     getActivePane : function()
25578     {
25579         var r = false;
25580         Roo.each(this.panes, function(p) {
25581             if(p.el.hasClass('active')){
25582                 r = p;
25583                 return false;
25584             }
25585             
25586             return;
25587         });
25588         
25589         return r;
25590     }
25591     
25592     
25593 });
25594
25595  
25596 /*
25597  * - LGPL
25598  *
25599  * Tab pane
25600  * 
25601  */
25602 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25603 /**
25604  * @class Roo.bootstrap.TabPane
25605  * @extends Roo.bootstrap.Component
25606  * Bootstrap TabPane class
25607  * @cfg {Boolean} active (false | true) Default false
25608  * @cfg {String} title title of panel
25609
25610  * 
25611  * @constructor
25612  * Create a new TabPane
25613  * @param {Object} config The config object
25614  */
25615
25616 Roo.bootstrap.dash.TabPane = function(config){
25617     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25618     
25619     this.addEvents({
25620         // raw events
25621         /**
25622          * @event activate
25623          * When a pane is activated
25624          * @param {Roo.bootstrap.dash.TabPane} pane
25625          */
25626         "activate" : true
25627          
25628     });
25629 };
25630
25631 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25632     
25633     active : false,
25634     title : '',
25635     
25636     // the tabBox that this is attached to.
25637     tab : false,
25638      
25639     getAutoCreate : function() 
25640     {
25641         var cfg = {
25642             tag: 'div',
25643             cls: 'tab-pane'
25644         };
25645         
25646         if(this.active){
25647             cfg.cls += ' active';
25648         }
25649         
25650         return cfg;
25651     },
25652     initEvents  : function()
25653     {
25654         //Roo.log('trigger add pane handler');
25655         this.parent().fireEvent('addpane', this)
25656     },
25657     
25658      /**
25659      * Updates the tab title 
25660      * @param {String} html to set the title to.
25661      */
25662     setTitle: function(str)
25663     {
25664         if (!this.tab) {
25665             return;
25666         }
25667         this.title = str;
25668         this.tab.select('a', true).first().dom.innerHTML = str;
25669         
25670     }
25671     
25672     
25673     
25674 });
25675
25676  
25677
25678
25679  /*
25680  * - LGPL
25681  *
25682  * menu
25683  * 
25684  */
25685 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25686
25687 /**
25688  * @class Roo.bootstrap.menu.Menu
25689  * @extends Roo.bootstrap.Component
25690  * Bootstrap Menu class - container for Menu
25691  * @cfg {String} html Text of the menu
25692  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25693  * @cfg {String} icon Font awesome icon
25694  * @cfg {String} pos Menu align to (top | bottom) default bottom
25695  * 
25696  * 
25697  * @constructor
25698  * Create a new Menu
25699  * @param {Object} config The config object
25700  */
25701
25702
25703 Roo.bootstrap.menu.Menu = function(config){
25704     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25705     
25706     this.addEvents({
25707         /**
25708          * @event beforeshow
25709          * Fires before this menu is displayed
25710          * @param {Roo.bootstrap.menu.Menu} this
25711          */
25712         beforeshow : true,
25713         /**
25714          * @event beforehide
25715          * Fires before this menu is hidden
25716          * @param {Roo.bootstrap.menu.Menu} this
25717          */
25718         beforehide : true,
25719         /**
25720          * @event show
25721          * Fires after this menu is displayed
25722          * @param {Roo.bootstrap.menu.Menu} this
25723          */
25724         show : true,
25725         /**
25726          * @event hide
25727          * Fires after this menu is hidden
25728          * @param {Roo.bootstrap.menu.Menu} this
25729          */
25730         hide : true,
25731         /**
25732          * @event click
25733          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25734          * @param {Roo.bootstrap.menu.Menu} this
25735          * @param {Roo.EventObject} e
25736          */
25737         click : true
25738     });
25739     
25740 };
25741
25742 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25743     
25744     submenu : false,
25745     html : '',
25746     weight : 'default',
25747     icon : false,
25748     pos : 'bottom',
25749     
25750     
25751     getChildContainer : function() {
25752         if(this.isSubMenu){
25753             return this.el;
25754         }
25755         
25756         return this.el.select('ul.dropdown-menu', true).first();  
25757     },
25758     
25759     getAutoCreate : function()
25760     {
25761         var text = [
25762             {
25763                 tag : 'span',
25764                 cls : 'roo-menu-text',
25765                 html : this.html
25766             }
25767         ];
25768         
25769         if(this.icon){
25770             text.unshift({
25771                 tag : 'i',
25772                 cls : 'fa ' + this.icon
25773             })
25774         }
25775         
25776         
25777         var cfg = {
25778             tag : 'div',
25779             cls : 'btn-group',
25780             cn : [
25781                 {
25782                     tag : 'button',
25783                     cls : 'dropdown-button btn btn-' + this.weight,
25784                     cn : text
25785                 },
25786                 {
25787                     tag : 'button',
25788                     cls : 'dropdown-toggle btn btn-' + this.weight,
25789                     cn : [
25790                         {
25791                             tag : 'span',
25792                             cls : 'caret'
25793                         }
25794                     ]
25795                 },
25796                 {
25797                     tag : 'ul',
25798                     cls : 'dropdown-menu'
25799                 }
25800             ]
25801             
25802         };
25803         
25804         if(this.pos == 'top'){
25805             cfg.cls += ' dropup';
25806         }
25807         
25808         if(this.isSubMenu){
25809             cfg = {
25810                 tag : 'ul',
25811                 cls : 'dropdown-menu'
25812             }
25813         }
25814         
25815         return cfg;
25816     },
25817     
25818     onRender : function(ct, position)
25819     {
25820         this.isSubMenu = ct.hasClass('dropdown-submenu');
25821         
25822         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25823     },
25824     
25825     initEvents : function() 
25826     {
25827         if(this.isSubMenu){
25828             return;
25829         }
25830         
25831         this.hidden = true;
25832         
25833         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25834         this.triggerEl.on('click', this.onTriggerPress, this);
25835         
25836         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25837         this.buttonEl.on('click', this.onClick, this);
25838         
25839     },
25840     
25841     list : function()
25842     {
25843         if(this.isSubMenu){
25844             return this.el;
25845         }
25846         
25847         return this.el.select('ul.dropdown-menu', true).first();
25848     },
25849     
25850     onClick : function(e)
25851     {
25852         this.fireEvent("click", this, e);
25853     },
25854     
25855     onTriggerPress  : function(e)
25856     {   
25857         if (this.isVisible()) {
25858             this.hide();
25859         } else {
25860             this.show();
25861         }
25862     },
25863     
25864     isVisible : function(){
25865         return !this.hidden;
25866     },
25867     
25868     show : function()
25869     {
25870         this.fireEvent("beforeshow", this);
25871         
25872         this.hidden = false;
25873         this.el.addClass('open');
25874         
25875         Roo.get(document).on("mouseup", this.onMouseUp, this);
25876         
25877         this.fireEvent("show", this);
25878         
25879         
25880     },
25881     
25882     hide : function()
25883     {
25884         this.fireEvent("beforehide", this);
25885         
25886         this.hidden = true;
25887         this.el.removeClass('open');
25888         
25889         Roo.get(document).un("mouseup", this.onMouseUp);
25890         
25891         this.fireEvent("hide", this);
25892     },
25893     
25894     onMouseUp : function()
25895     {
25896         this.hide();
25897     }
25898     
25899 });
25900
25901  
25902  /*
25903  * - LGPL
25904  *
25905  * menu item
25906  * 
25907  */
25908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25909
25910 /**
25911  * @class Roo.bootstrap.menu.Item
25912  * @extends Roo.bootstrap.Component
25913  * Bootstrap MenuItem class
25914  * @cfg {Boolean} submenu (true | false) default false
25915  * @cfg {String} html text of the item
25916  * @cfg {String} href the link
25917  * @cfg {Boolean} disable (true | false) default false
25918  * @cfg {Boolean} preventDefault (true | false) default true
25919  * @cfg {String} icon Font awesome icon
25920  * @cfg {String} pos Submenu align to (left | right) default right 
25921  * 
25922  * 
25923  * @constructor
25924  * Create a new Item
25925  * @param {Object} config The config object
25926  */
25927
25928
25929 Roo.bootstrap.menu.Item = function(config){
25930     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25931     this.addEvents({
25932         /**
25933          * @event mouseover
25934          * Fires when the mouse is hovering over this menu
25935          * @param {Roo.bootstrap.menu.Item} this
25936          * @param {Roo.EventObject} e
25937          */
25938         mouseover : true,
25939         /**
25940          * @event mouseout
25941          * Fires when the mouse exits this menu
25942          * @param {Roo.bootstrap.menu.Item} this
25943          * @param {Roo.EventObject} e
25944          */
25945         mouseout : true,
25946         // raw events
25947         /**
25948          * @event click
25949          * The raw click event for the entire grid.
25950          * @param {Roo.EventObject} e
25951          */
25952         click : true
25953     });
25954 };
25955
25956 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25957     
25958     submenu : false,
25959     href : '',
25960     html : '',
25961     preventDefault: true,
25962     disable : false,
25963     icon : false,
25964     pos : 'right',
25965     
25966     getAutoCreate : function()
25967     {
25968         var text = [
25969             {
25970                 tag : 'span',
25971                 cls : 'roo-menu-item-text',
25972                 html : this.html
25973             }
25974         ];
25975         
25976         if(this.icon){
25977             text.unshift({
25978                 tag : 'i',
25979                 cls : 'fa ' + this.icon
25980             })
25981         }
25982         
25983         var cfg = {
25984             tag : 'li',
25985             cn : [
25986                 {
25987                     tag : 'a',
25988                     href : this.href || '#',
25989                     cn : text
25990                 }
25991             ]
25992         };
25993         
25994         if(this.disable){
25995             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25996         }
25997         
25998         if(this.submenu){
25999             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26000             
26001             if(this.pos == 'left'){
26002                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26003             }
26004         }
26005         
26006         return cfg;
26007     },
26008     
26009     initEvents : function() 
26010     {
26011         this.el.on('mouseover', this.onMouseOver, this);
26012         this.el.on('mouseout', this.onMouseOut, this);
26013         
26014         this.el.select('a', true).first().on('click', this.onClick, this);
26015         
26016     },
26017     
26018     onClick : function(e)
26019     {
26020         if(this.preventDefault){
26021             e.preventDefault();
26022         }
26023         
26024         this.fireEvent("click", this, e);
26025     },
26026     
26027     onMouseOver : function(e)
26028     {
26029         if(this.submenu && this.pos == 'left'){
26030             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26031         }
26032         
26033         this.fireEvent("mouseover", this, e);
26034     },
26035     
26036     onMouseOut : function(e)
26037     {
26038         this.fireEvent("mouseout", this, e);
26039     }
26040 });
26041
26042  
26043
26044  /*
26045  * - LGPL
26046  *
26047  * menu separator
26048  * 
26049  */
26050 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26051
26052 /**
26053  * @class Roo.bootstrap.menu.Separator
26054  * @extends Roo.bootstrap.Component
26055  * Bootstrap Separator class
26056  * 
26057  * @constructor
26058  * Create a new Separator
26059  * @param {Object} config The config object
26060  */
26061
26062
26063 Roo.bootstrap.menu.Separator = function(config){
26064     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26065 };
26066
26067 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26068     
26069     getAutoCreate : function(){
26070         var cfg = {
26071             tag : 'li',
26072             cls: 'divider'
26073         };
26074         
26075         return cfg;
26076     }
26077    
26078 });
26079
26080  
26081
26082  /*
26083  * - LGPL
26084  *
26085  * Tooltip
26086  * 
26087  */
26088
26089 /**
26090  * @class Roo.bootstrap.Tooltip
26091  * Bootstrap Tooltip class
26092  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26093  * to determine which dom element triggers the tooltip.
26094  * 
26095  * It needs to add support for additional attributes like tooltip-position
26096  * 
26097  * @constructor
26098  * Create a new Toolti
26099  * @param {Object} config The config object
26100  */
26101
26102 Roo.bootstrap.Tooltip = function(config){
26103     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26104     
26105     this.alignment = Roo.bootstrap.Tooltip.alignment;
26106     
26107     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26108         this.alignment = config.alignment;
26109     }
26110     
26111 };
26112
26113 Roo.apply(Roo.bootstrap.Tooltip, {
26114     /**
26115      * @function init initialize tooltip monitoring.
26116      * @static
26117      */
26118     currentEl : false,
26119     currentTip : false,
26120     currentRegion : false,
26121     
26122     //  init : delay?
26123     
26124     init : function()
26125     {
26126         Roo.get(document).on('mouseover', this.enter ,this);
26127         Roo.get(document).on('mouseout', this.leave, this);
26128          
26129         
26130         this.currentTip = new Roo.bootstrap.Tooltip();
26131     },
26132     
26133     enter : function(ev)
26134     {
26135         var dom = ev.getTarget();
26136         
26137         //Roo.log(['enter',dom]);
26138         var el = Roo.fly(dom);
26139         if (this.currentEl) {
26140             //Roo.log(dom);
26141             //Roo.log(this.currentEl);
26142             //Roo.log(this.currentEl.contains(dom));
26143             if (this.currentEl == el) {
26144                 return;
26145             }
26146             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26147                 return;
26148             }
26149
26150         }
26151         
26152         if (this.currentTip.el) {
26153             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26154         }    
26155         //Roo.log(ev);
26156         
26157         if(!el || el.dom == document){
26158             return;
26159         }
26160         
26161         var bindEl = el;
26162         
26163         // you can not look for children, as if el is the body.. then everythign is the child..
26164         if (!el.attr('tooltip')) { //
26165             if (!el.select("[tooltip]").elements.length) {
26166                 return;
26167             }
26168             // is the mouse over this child...?
26169             bindEl = el.select("[tooltip]").first();
26170             var xy = ev.getXY();
26171             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26172                 //Roo.log("not in region.");
26173                 return;
26174             }
26175             //Roo.log("child element over..");
26176             
26177         }
26178         this.currentEl = bindEl;
26179         this.currentTip.bind(bindEl);
26180         this.currentRegion = Roo.lib.Region.getRegion(dom);
26181         this.currentTip.enter();
26182         
26183     },
26184     leave : function(ev)
26185     {
26186         var dom = ev.getTarget();
26187         //Roo.log(['leave',dom]);
26188         if (!this.currentEl) {
26189             return;
26190         }
26191         
26192         
26193         if (dom != this.currentEl.dom) {
26194             return;
26195         }
26196         var xy = ev.getXY();
26197         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26198             return;
26199         }
26200         // only activate leave if mouse cursor is outside... bounding box..
26201         
26202         
26203         
26204         
26205         if (this.currentTip) {
26206             this.currentTip.leave();
26207         }
26208         //Roo.log('clear currentEl');
26209         this.currentEl = false;
26210         
26211         
26212     },
26213     alignment : {
26214         'left' : ['r-l', [-2,0], 'right'],
26215         'right' : ['l-r', [2,0], 'left'],
26216         'bottom' : ['t-b', [0,2], 'top'],
26217         'top' : [ 'b-t', [0,-2], 'bottom']
26218     }
26219     
26220 });
26221
26222
26223 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26224     
26225     
26226     bindEl : false,
26227     
26228     delay : null, // can be { show : 300 , hide: 500}
26229     
26230     timeout : null,
26231     
26232     hoverState : null, //???
26233     
26234     placement : 'bottom', 
26235     
26236     alignment : false,
26237     
26238     getAutoCreate : function(){
26239     
26240         var cfg = {
26241            cls : 'tooltip',
26242            role : 'tooltip',
26243            cn : [
26244                 {
26245                     cls : 'tooltip-arrow'
26246                 },
26247                 {
26248                     cls : 'tooltip-inner'
26249                 }
26250            ]
26251         };
26252         
26253         return cfg;
26254     },
26255     bind : function(el)
26256     {
26257         this.bindEl = el;
26258     },
26259       
26260     
26261     enter : function () {
26262        
26263         if (this.timeout != null) {
26264             clearTimeout(this.timeout);
26265         }
26266         
26267         this.hoverState = 'in';
26268          //Roo.log("enter - show");
26269         if (!this.delay || !this.delay.show) {
26270             this.show();
26271             return;
26272         }
26273         var _t = this;
26274         this.timeout = setTimeout(function () {
26275             if (_t.hoverState == 'in') {
26276                 _t.show();
26277             }
26278         }, this.delay.show);
26279     },
26280     leave : function()
26281     {
26282         clearTimeout(this.timeout);
26283     
26284         this.hoverState = 'out';
26285          if (!this.delay || !this.delay.hide) {
26286             this.hide();
26287             return;
26288         }
26289        
26290         var _t = this;
26291         this.timeout = setTimeout(function () {
26292             //Roo.log("leave - timeout");
26293             
26294             if (_t.hoverState == 'out') {
26295                 _t.hide();
26296                 Roo.bootstrap.Tooltip.currentEl = false;
26297             }
26298         }, delay);
26299     },
26300     
26301     show : function (msg)
26302     {
26303         if (!this.el) {
26304             this.render(document.body);
26305         }
26306         // set content.
26307         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26308         
26309         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26310         
26311         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26312         
26313         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26314         
26315         var placement = typeof this.placement == 'function' ?
26316             this.placement.call(this, this.el, on_el) :
26317             this.placement;
26318             
26319         var autoToken = /\s?auto?\s?/i;
26320         var autoPlace = autoToken.test(placement);
26321         if (autoPlace) {
26322             placement = placement.replace(autoToken, '') || 'top';
26323         }
26324         
26325         //this.el.detach()
26326         //this.el.setXY([0,0]);
26327         this.el.show();
26328         //this.el.dom.style.display='block';
26329         
26330         //this.el.appendTo(on_el);
26331         
26332         var p = this.getPosition();
26333         var box = this.el.getBox();
26334         
26335         if (autoPlace) {
26336             // fixme..
26337         }
26338         
26339         var align = this.alignment[placement];
26340         
26341         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26342         
26343         if(placement == 'top' || placement == 'bottom'){
26344             if(xy[0] < 0){
26345                 placement = 'right';
26346             }
26347             
26348             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26349                 placement = 'left';
26350             }
26351             
26352             var scroll = Roo.select('body', true).first().getScroll();
26353             
26354             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26355                 placement = 'top';
26356             }
26357             
26358             align = this.alignment[placement];
26359         }
26360         
26361         this.el.alignTo(this.bindEl, align[0],align[1]);
26362         //var arrow = this.el.select('.arrow',true).first();
26363         //arrow.set(align[2], 
26364         
26365         this.el.addClass(placement);
26366         
26367         this.el.addClass('in fade');
26368         
26369         this.hoverState = null;
26370         
26371         if (this.el.hasClass('fade')) {
26372             // fade it?
26373         }
26374         
26375     },
26376     hide : function()
26377     {
26378          
26379         if (!this.el) {
26380             return;
26381         }
26382         //this.el.setXY([0,0]);
26383         this.el.removeClass('in');
26384         //this.el.hide();
26385         
26386     }
26387     
26388 });
26389  
26390
26391  /*
26392  * - LGPL
26393  *
26394  * Location Picker
26395  * 
26396  */
26397
26398 /**
26399  * @class Roo.bootstrap.LocationPicker
26400  * @extends Roo.bootstrap.Component
26401  * Bootstrap LocationPicker class
26402  * @cfg {Number} latitude Position when init default 0
26403  * @cfg {Number} longitude Position when init default 0
26404  * @cfg {Number} zoom default 15
26405  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26406  * @cfg {Boolean} mapTypeControl default false
26407  * @cfg {Boolean} disableDoubleClickZoom default false
26408  * @cfg {Boolean} scrollwheel default true
26409  * @cfg {Boolean} streetViewControl default false
26410  * @cfg {Number} radius default 0
26411  * @cfg {String} locationName
26412  * @cfg {Boolean} draggable default true
26413  * @cfg {Boolean} enableAutocomplete default false
26414  * @cfg {Boolean} enableReverseGeocode default true
26415  * @cfg {String} markerTitle
26416  * 
26417  * @constructor
26418  * Create a new LocationPicker
26419  * @param {Object} config The config object
26420  */
26421
26422
26423 Roo.bootstrap.LocationPicker = function(config){
26424     
26425     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26426     
26427     this.addEvents({
26428         /**
26429          * @event initial
26430          * Fires when the picker initialized.
26431          * @param {Roo.bootstrap.LocationPicker} this
26432          * @param {Google Location} location
26433          */
26434         initial : true,
26435         /**
26436          * @event positionchanged
26437          * Fires when the picker position changed.
26438          * @param {Roo.bootstrap.LocationPicker} this
26439          * @param {Google Location} location
26440          */
26441         positionchanged : true,
26442         /**
26443          * @event resize
26444          * Fires when the map resize.
26445          * @param {Roo.bootstrap.LocationPicker} this
26446          */
26447         resize : true,
26448         /**
26449          * @event show
26450          * Fires when the map show.
26451          * @param {Roo.bootstrap.LocationPicker} this
26452          */
26453         show : true,
26454         /**
26455          * @event hide
26456          * Fires when the map hide.
26457          * @param {Roo.bootstrap.LocationPicker} this
26458          */
26459         hide : true,
26460         /**
26461          * @event mapClick
26462          * Fires when click the map.
26463          * @param {Roo.bootstrap.LocationPicker} this
26464          * @param {Map event} e
26465          */
26466         mapClick : true,
26467         /**
26468          * @event mapRightClick
26469          * Fires when right click the map.
26470          * @param {Roo.bootstrap.LocationPicker} this
26471          * @param {Map event} e
26472          */
26473         mapRightClick : true,
26474         /**
26475          * @event markerClick
26476          * Fires when click the marker.
26477          * @param {Roo.bootstrap.LocationPicker} this
26478          * @param {Map event} e
26479          */
26480         markerClick : true,
26481         /**
26482          * @event markerRightClick
26483          * Fires when right click the marker.
26484          * @param {Roo.bootstrap.LocationPicker} this
26485          * @param {Map event} e
26486          */
26487         markerRightClick : true,
26488         /**
26489          * @event OverlayViewDraw
26490          * Fires when OverlayView Draw
26491          * @param {Roo.bootstrap.LocationPicker} this
26492          */
26493         OverlayViewDraw : true,
26494         /**
26495          * @event OverlayViewOnAdd
26496          * Fires when OverlayView Draw
26497          * @param {Roo.bootstrap.LocationPicker} this
26498          */
26499         OverlayViewOnAdd : true,
26500         /**
26501          * @event OverlayViewOnRemove
26502          * Fires when OverlayView Draw
26503          * @param {Roo.bootstrap.LocationPicker} this
26504          */
26505         OverlayViewOnRemove : true,
26506         /**
26507          * @event OverlayViewShow
26508          * Fires when OverlayView Draw
26509          * @param {Roo.bootstrap.LocationPicker} this
26510          * @param {Pixel} cpx
26511          */
26512         OverlayViewShow : true,
26513         /**
26514          * @event OverlayViewHide
26515          * Fires when OverlayView Draw
26516          * @param {Roo.bootstrap.LocationPicker} this
26517          */
26518         OverlayViewHide : true,
26519         /**
26520          * @event loadexception
26521          * Fires when load google lib failed.
26522          * @param {Roo.bootstrap.LocationPicker} this
26523          */
26524         loadexception : true
26525     });
26526         
26527 };
26528
26529 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26530     
26531     gMapContext: false,
26532     
26533     latitude: 0,
26534     longitude: 0,
26535     zoom: 15,
26536     mapTypeId: false,
26537     mapTypeControl: false,
26538     disableDoubleClickZoom: false,
26539     scrollwheel: true,
26540     streetViewControl: false,
26541     radius: 0,
26542     locationName: '',
26543     draggable: true,
26544     enableAutocomplete: false,
26545     enableReverseGeocode: true,
26546     markerTitle: '',
26547     
26548     getAutoCreate: function()
26549     {
26550
26551         var cfg = {
26552             tag: 'div',
26553             cls: 'roo-location-picker'
26554         };
26555         
26556         return cfg
26557     },
26558     
26559     initEvents: function(ct, position)
26560     {       
26561         if(!this.el.getWidth() || this.isApplied()){
26562             return;
26563         }
26564         
26565         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26566         
26567         this.initial();
26568     },
26569     
26570     initial: function()
26571     {
26572         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26573             this.fireEvent('loadexception', this);
26574             return;
26575         }
26576         
26577         if(!this.mapTypeId){
26578             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26579         }
26580         
26581         this.gMapContext = this.GMapContext();
26582         
26583         this.initOverlayView();
26584         
26585         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26586         
26587         var _this = this;
26588                 
26589         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26590             _this.setPosition(_this.gMapContext.marker.position);
26591         });
26592         
26593         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26594             _this.fireEvent('mapClick', this, event);
26595             
26596         });
26597
26598         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26599             _this.fireEvent('mapRightClick', this, event);
26600             
26601         });
26602         
26603         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26604             _this.fireEvent('markerClick', this, event);
26605             
26606         });
26607
26608         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26609             _this.fireEvent('markerRightClick', this, event);
26610             
26611         });
26612         
26613         this.setPosition(this.gMapContext.location);
26614         
26615         this.fireEvent('initial', this, this.gMapContext.location);
26616     },
26617     
26618     initOverlayView: function()
26619     {
26620         var _this = this;
26621         
26622         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26623             
26624             draw: function()
26625             {
26626                 _this.fireEvent('OverlayViewDraw', _this);
26627             },
26628             
26629             onAdd: function()
26630             {
26631                 _this.fireEvent('OverlayViewOnAdd', _this);
26632             },
26633             
26634             onRemove: function()
26635             {
26636                 _this.fireEvent('OverlayViewOnRemove', _this);
26637             },
26638             
26639             show: function(cpx)
26640             {
26641                 _this.fireEvent('OverlayViewShow', _this, cpx);
26642             },
26643             
26644             hide: function()
26645             {
26646                 _this.fireEvent('OverlayViewHide', _this);
26647             }
26648             
26649         });
26650     },
26651     
26652     fromLatLngToContainerPixel: function(event)
26653     {
26654         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26655     },
26656     
26657     isApplied: function() 
26658     {
26659         return this.getGmapContext() == false ? false : true;
26660     },
26661     
26662     getGmapContext: function() 
26663     {
26664         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26665     },
26666     
26667     GMapContext: function() 
26668     {
26669         var position = new google.maps.LatLng(this.latitude, this.longitude);
26670         
26671         var _map = new google.maps.Map(this.el.dom, {
26672             center: position,
26673             zoom: this.zoom,
26674             mapTypeId: this.mapTypeId,
26675             mapTypeControl: this.mapTypeControl,
26676             disableDoubleClickZoom: this.disableDoubleClickZoom,
26677             scrollwheel: this.scrollwheel,
26678             streetViewControl: this.streetViewControl,
26679             locationName: this.locationName,
26680             draggable: this.draggable,
26681             enableAutocomplete: this.enableAutocomplete,
26682             enableReverseGeocode: this.enableReverseGeocode
26683         });
26684         
26685         var _marker = new google.maps.Marker({
26686             position: position,
26687             map: _map,
26688             title: this.markerTitle,
26689             draggable: this.draggable
26690         });
26691         
26692         return {
26693             map: _map,
26694             marker: _marker,
26695             circle: null,
26696             location: position,
26697             radius: this.radius,
26698             locationName: this.locationName,
26699             addressComponents: {
26700                 formatted_address: null,
26701                 addressLine1: null,
26702                 addressLine2: null,
26703                 streetName: null,
26704                 streetNumber: null,
26705                 city: null,
26706                 district: null,
26707                 state: null,
26708                 stateOrProvince: null
26709             },
26710             settings: this,
26711             domContainer: this.el.dom,
26712             geodecoder: new google.maps.Geocoder()
26713         };
26714     },
26715     
26716     drawCircle: function(center, radius, options) 
26717     {
26718         if (this.gMapContext.circle != null) {
26719             this.gMapContext.circle.setMap(null);
26720         }
26721         if (radius > 0) {
26722             radius *= 1;
26723             options = Roo.apply({}, options, {
26724                 strokeColor: "#0000FF",
26725                 strokeOpacity: .35,
26726                 strokeWeight: 2,
26727                 fillColor: "#0000FF",
26728                 fillOpacity: .2
26729             });
26730             
26731             options.map = this.gMapContext.map;
26732             options.radius = radius;
26733             options.center = center;
26734             this.gMapContext.circle = new google.maps.Circle(options);
26735             return this.gMapContext.circle;
26736         }
26737         
26738         return null;
26739     },
26740     
26741     setPosition: function(location) 
26742     {
26743         this.gMapContext.location = location;
26744         this.gMapContext.marker.setPosition(location);
26745         this.gMapContext.map.panTo(location);
26746         this.drawCircle(location, this.gMapContext.radius, {});
26747         
26748         var _this = this;
26749         
26750         if (this.gMapContext.settings.enableReverseGeocode) {
26751             this.gMapContext.geodecoder.geocode({
26752                 latLng: this.gMapContext.location
26753             }, function(results, status) {
26754                 
26755                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26756                     _this.gMapContext.locationName = results[0].formatted_address;
26757                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26758                     
26759                     _this.fireEvent('positionchanged', this, location);
26760                 }
26761             });
26762             
26763             return;
26764         }
26765         
26766         this.fireEvent('positionchanged', this, location);
26767     },
26768     
26769     resize: function()
26770     {
26771         google.maps.event.trigger(this.gMapContext.map, "resize");
26772         
26773         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26774         
26775         this.fireEvent('resize', this);
26776     },
26777     
26778     setPositionByLatLng: function(latitude, longitude)
26779     {
26780         this.setPosition(new google.maps.LatLng(latitude, longitude));
26781     },
26782     
26783     getCurrentPosition: function() 
26784     {
26785         return {
26786             latitude: this.gMapContext.location.lat(),
26787             longitude: this.gMapContext.location.lng()
26788         };
26789     },
26790     
26791     getAddressName: function() 
26792     {
26793         return this.gMapContext.locationName;
26794     },
26795     
26796     getAddressComponents: function() 
26797     {
26798         return this.gMapContext.addressComponents;
26799     },
26800     
26801     address_component_from_google_geocode: function(address_components) 
26802     {
26803         var result = {};
26804         
26805         for (var i = 0; i < address_components.length; i++) {
26806             var component = address_components[i];
26807             if (component.types.indexOf("postal_code") >= 0) {
26808                 result.postalCode = component.short_name;
26809             } else if (component.types.indexOf("street_number") >= 0) {
26810                 result.streetNumber = component.short_name;
26811             } else if (component.types.indexOf("route") >= 0) {
26812                 result.streetName = component.short_name;
26813             } else if (component.types.indexOf("neighborhood") >= 0) {
26814                 result.city = component.short_name;
26815             } else if (component.types.indexOf("locality") >= 0) {
26816                 result.city = component.short_name;
26817             } else if (component.types.indexOf("sublocality") >= 0) {
26818                 result.district = component.short_name;
26819             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26820                 result.stateOrProvince = component.short_name;
26821             } else if (component.types.indexOf("country") >= 0) {
26822                 result.country = component.short_name;
26823             }
26824         }
26825         
26826         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26827         result.addressLine2 = "";
26828         return result;
26829     },
26830     
26831     setZoomLevel: function(zoom)
26832     {
26833         this.gMapContext.map.setZoom(zoom);
26834     },
26835     
26836     show: function()
26837     {
26838         if(!this.el){
26839             return;
26840         }
26841         
26842         this.el.show();
26843         
26844         this.resize();
26845         
26846         this.fireEvent('show', this);
26847     },
26848     
26849     hide: function()
26850     {
26851         if(!this.el){
26852             return;
26853         }
26854         
26855         this.el.hide();
26856         
26857         this.fireEvent('hide', this);
26858     }
26859     
26860 });
26861
26862 Roo.apply(Roo.bootstrap.LocationPicker, {
26863     
26864     OverlayView : function(map, options)
26865     {
26866         options = options || {};
26867         
26868         this.setMap(map);
26869     }
26870     
26871     
26872 });/*
26873  * - LGPL
26874  *
26875  * Alert
26876  * 
26877  */
26878
26879 /**
26880  * @class Roo.bootstrap.Alert
26881  * @extends Roo.bootstrap.Component
26882  * Bootstrap Alert class
26883  * @cfg {String} title The title of alert
26884  * @cfg {String} html The content of alert
26885  * @cfg {String} weight (  success | info | warning | danger )
26886  * @cfg {String} faicon font-awesomeicon
26887  * 
26888  * @constructor
26889  * Create a new alert
26890  * @param {Object} config The config object
26891  */
26892
26893
26894 Roo.bootstrap.Alert = function(config){
26895     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26896     
26897 };
26898
26899 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26900     
26901     title: '',
26902     html: '',
26903     weight: false,
26904     faicon: false,
26905     
26906     getAutoCreate : function()
26907     {
26908         
26909         var cfg = {
26910             tag : 'div',
26911             cls : 'alert',
26912             cn : [
26913                 {
26914                     tag : 'i',
26915                     cls : 'roo-alert-icon'
26916                     
26917                 },
26918                 {
26919                     tag : 'b',
26920                     cls : 'roo-alert-title',
26921                     html : this.title
26922                 },
26923                 {
26924                     tag : 'span',
26925                     cls : 'roo-alert-text',
26926                     html : this.html
26927                 }
26928             ]
26929         };
26930         
26931         if(this.faicon){
26932             cfg.cn[0].cls += ' fa ' + this.faicon;
26933         }
26934         
26935         if(this.weight){
26936             cfg.cls += ' alert-' + this.weight;
26937         }
26938         
26939         return cfg;
26940     },
26941     
26942     initEvents: function() 
26943     {
26944         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26945     },
26946     
26947     setTitle : function(str)
26948     {
26949         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26950     },
26951     
26952     setText : function(str)
26953     {
26954         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26955     },
26956     
26957     setWeight : function(weight)
26958     {
26959         if(this.weight){
26960             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26961         }
26962         
26963         this.weight = weight;
26964         
26965         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26966     },
26967     
26968     setIcon : function(icon)
26969     {
26970         if(this.faicon){
26971             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26972         }
26973         
26974         this.faicon = icon;
26975         
26976         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26977     },
26978     
26979     hide: function() 
26980     {
26981         this.el.hide();   
26982     },
26983     
26984     show: function() 
26985     {  
26986         this.el.show();   
26987     }
26988     
26989 });
26990
26991  
26992 /*
26993 * Licence: LGPL
26994 */
26995
26996 /**
26997  * @class Roo.bootstrap.UploadCropbox
26998  * @extends Roo.bootstrap.Component
26999  * Bootstrap UploadCropbox class
27000  * @cfg {String} emptyText show when image has been loaded
27001  * @cfg {String} rotateNotify show when image too small to rotate
27002  * @cfg {Number} errorTimeout default 3000
27003  * @cfg {Number} minWidth default 300
27004  * @cfg {Number} minHeight default 300
27005  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27006  * @cfg {Boolean} isDocument (true|false) default false
27007  * @cfg {String} url action url
27008  * @cfg {String} paramName default 'imageUpload'
27009  * @cfg {String} method default POST
27010  * @cfg {Boolean} loadMask (true|false) default true
27011  * @cfg {Boolean} loadingText default 'Loading...'
27012  * 
27013  * @constructor
27014  * Create a new UploadCropbox
27015  * @param {Object} config The config object
27016  */
27017
27018 Roo.bootstrap.UploadCropbox = function(config){
27019     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27020     
27021     this.addEvents({
27022         /**
27023          * @event beforeselectfile
27024          * Fire before select file
27025          * @param {Roo.bootstrap.UploadCropbox} this
27026          */
27027         "beforeselectfile" : true,
27028         /**
27029          * @event initial
27030          * Fire after initEvent
27031          * @param {Roo.bootstrap.UploadCropbox} this
27032          */
27033         "initial" : true,
27034         /**
27035          * @event crop
27036          * Fire after initEvent
27037          * @param {Roo.bootstrap.UploadCropbox} this
27038          * @param {String} data
27039          */
27040         "crop" : true,
27041         /**
27042          * @event prepare
27043          * Fire when preparing the file data
27044          * @param {Roo.bootstrap.UploadCropbox} this
27045          * @param {Object} file
27046          */
27047         "prepare" : true,
27048         /**
27049          * @event exception
27050          * Fire when get exception
27051          * @param {Roo.bootstrap.UploadCropbox} this
27052          * @param {XMLHttpRequest} xhr
27053          */
27054         "exception" : true,
27055         /**
27056          * @event beforeloadcanvas
27057          * Fire before load the canvas
27058          * @param {Roo.bootstrap.UploadCropbox} this
27059          * @param {String} src
27060          */
27061         "beforeloadcanvas" : true,
27062         /**
27063          * @event trash
27064          * Fire when trash image
27065          * @param {Roo.bootstrap.UploadCropbox} this
27066          */
27067         "trash" : true,
27068         /**
27069          * @event download
27070          * Fire when download the image
27071          * @param {Roo.bootstrap.UploadCropbox} this
27072          */
27073         "download" : true,
27074         /**
27075          * @event footerbuttonclick
27076          * Fire when footerbuttonclick
27077          * @param {Roo.bootstrap.UploadCropbox} this
27078          * @param {String} type
27079          */
27080         "footerbuttonclick" : true,
27081         /**
27082          * @event resize
27083          * Fire when resize
27084          * @param {Roo.bootstrap.UploadCropbox} this
27085          */
27086         "resize" : true,
27087         /**
27088          * @event rotate
27089          * Fire when rotate the image
27090          * @param {Roo.bootstrap.UploadCropbox} this
27091          * @param {String} pos
27092          */
27093         "rotate" : true,
27094         /**
27095          * @event inspect
27096          * Fire when inspect the file
27097          * @param {Roo.bootstrap.UploadCropbox} this
27098          * @param {Object} file
27099          */
27100         "inspect" : true,
27101         /**
27102          * @event upload
27103          * Fire when xhr upload the file
27104          * @param {Roo.bootstrap.UploadCropbox} this
27105          * @param {Object} data
27106          */
27107         "upload" : true,
27108         /**
27109          * @event arrange
27110          * Fire when arrange the file data
27111          * @param {Roo.bootstrap.UploadCropbox} this
27112          * @param {Object} formData
27113          */
27114         "arrange" : true
27115     });
27116     
27117     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27118 };
27119
27120 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27121     
27122     emptyText : 'Click to upload image',
27123     rotateNotify : 'Image is too small to rotate',
27124     errorTimeout : 3000,
27125     scale : 0,
27126     baseScale : 1,
27127     rotate : 0,
27128     dragable : false,
27129     pinching : false,
27130     mouseX : 0,
27131     mouseY : 0,
27132     cropData : false,
27133     minWidth : 300,
27134     minHeight : 300,
27135     file : false,
27136     exif : {},
27137     baseRotate : 1,
27138     cropType : 'image/jpeg',
27139     buttons : false,
27140     canvasLoaded : false,
27141     isDocument : false,
27142     method : 'POST',
27143     paramName : 'imageUpload',
27144     loadMask : true,
27145     loadingText : 'Loading...',
27146     maskEl : false,
27147     
27148     getAutoCreate : function()
27149     {
27150         var cfg = {
27151             tag : 'div',
27152             cls : 'roo-upload-cropbox',
27153             cn : [
27154                 {
27155                     tag : 'input',
27156                     cls : 'roo-upload-cropbox-selector',
27157                     type : 'file'
27158                 },
27159                 {
27160                     tag : 'div',
27161                     cls : 'roo-upload-cropbox-body',
27162                     style : 'cursor:pointer',
27163                     cn : [
27164                         {
27165                             tag : 'div',
27166                             cls : 'roo-upload-cropbox-preview'
27167                         },
27168                         {
27169                             tag : 'div',
27170                             cls : 'roo-upload-cropbox-thumb'
27171                         },
27172                         {
27173                             tag : 'div',
27174                             cls : 'roo-upload-cropbox-empty-notify',
27175                             html : this.emptyText
27176                         },
27177                         {
27178                             tag : 'div',
27179                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27180                             html : this.rotateNotify
27181                         }
27182                     ]
27183                 },
27184                 {
27185                     tag : 'div',
27186                     cls : 'roo-upload-cropbox-footer',
27187                     cn : {
27188                         tag : 'div',
27189                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27190                         cn : []
27191                     }
27192                 }
27193             ]
27194         };
27195         
27196         return cfg;
27197     },
27198     
27199     onRender : function(ct, position)
27200     {
27201         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27202         
27203         if (this.buttons.length) {
27204             
27205             Roo.each(this.buttons, function(bb) {
27206                 
27207                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27208                 
27209                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27210                 
27211             }, this);
27212         }
27213         
27214         if(this.loadMask){
27215             this.maskEl = this.el;
27216         }
27217     },
27218     
27219     initEvents : function()
27220     {
27221         this.urlAPI = (window.createObjectURL && window) || 
27222                                 (window.URL && URL.revokeObjectURL && URL) || 
27223                                 (window.webkitURL && webkitURL);
27224                         
27225         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27226         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27227         
27228         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27229         this.selectorEl.hide();
27230         
27231         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27232         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27233         
27234         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27235         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27236         this.thumbEl.hide();
27237         
27238         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27239         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27240         
27241         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27242         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27243         this.errorEl.hide();
27244         
27245         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27246         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27247         this.footerEl.hide();
27248         
27249         this.setThumbBoxSize();
27250         
27251         this.bind();
27252         
27253         this.resize();
27254         
27255         this.fireEvent('initial', this);
27256     },
27257
27258     bind : function()
27259     {
27260         var _this = this;
27261         
27262         window.addEventListener("resize", function() { _this.resize(); } );
27263         
27264         this.bodyEl.on('click', this.beforeSelectFile, this);
27265         
27266         if(Roo.isTouch){
27267             this.bodyEl.on('touchstart', this.onTouchStart, this);
27268             this.bodyEl.on('touchmove', this.onTouchMove, this);
27269             this.bodyEl.on('touchend', this.onTouchEnd, this);
27270         }
27271         
27272         if(!Roo.isTouch){
27273             this.bodyEl.on('mousedown', this.onMouseDown, this);
27274             this.bodyEl.on('mousemove', this.onMouseMove, this);
27275             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27276             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27277             Roo.get(document).on('mouseup', this.onMouseUp, this);
27278         }
27279         
27280         this.selectorEl.on('change', this.onFileSelected, this);
27281     },
27282     
27283     reset : function()
27284     {    
27285         this.scale = 0;
27286         this.baseScale = 1;
27287         this.rotate = 0;
27288         this.baseRotate = 1;
27289         this.dragable = false;
27290         this.pinching = false;
27291         this.mouseX = 0;
27292         this.mouseY = 0;
27293         this.cropData = false;
27294         this.notifyEl.dom.innerHTML = this.emptyText;
27295         
27296         this.selectorEl.dom.value = '';
27297         
27298     },
27299     
27300     resize : function()
27301     {
27302         if(this.fireEvent('resize', this) != false){
27303             this.setThumbBoxPosition();
27304             this.setCanvasPosition();
27305         }
27306     },
27307     
27308     onFooterButtonClick : function(e, el, o, type)
27309     {
27310         switch (type) {
27311             case 'rotate-left' :
27312                 this.onRotateLeft(e);
27313                 break;
27314             case 'rotate-right' :
27315                 this.onRotateRight(e);
27316                 break;
27317             case 'picture' :
27318                 this.beforeSelectFile(e);
27319                 break;
27320             case 'trash' :
27321                 this.trash(e);
27322                 break;
27323             case 'crop' :
27324                 this.crop(e);
27325                 break;
27326             case 'download' :
27327                 this.download(e);
27328                 break;
27329             default :
27330                 break;
27331         }
27332         
27333         this.fireEvent('footerbuttonclick', this, type);
27334     },
27335     
27336     beforeSelectFile : function(e)
27337     {
27338         e.preventDefault();
27339         
27340         if(this.fireEvent('beforeselectfile', this) != false){
27341             this.selectorEl.dom.click();
27342         }
27343     },
27344     
27345     onFileSelected : function(e)
27346     {
27347         e.preventDefault();
27348         
27349         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27350             return;
27351         }
27352         
27353         var file = this.selectorEl.dom.files[0];
27354         
27355         if(this.fireEvent('inspect', this, file) != false){
27356             this.prepare(file);
27357         }
27358         
27359     },
27360     
27361     trash : function(e)
27362     {
27363         this.fireEvent('trash', this);
27364     },
27365     
27366     download : function(e)
27367     {
27368         this.fireEvent('download', this);
27369     },
27370     
27371     loadCanvas : function(src)
27372     {   
27373         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27374             
27375             this.reset();
27376             
27377             this.imageEl = document.createElement('img');
27378             
27379             var _this = this;
27380             
27381             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27382             
27383             this.imageEl.src = src;
27384         }
27385     },
27386     
27387     onLoadCanvas : function()
27388     {   
27389         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27390         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27391         
27392         this.bodyEl.un('click', this.beforeSelectFile, this);
27393         
27394         this.notifyEl.hide();
27395         this.thumbEl.show();
27396         this.footerEl.show();
27397         
27398         this.baseRotateLevel();
27399         
27400         if(this.isDocument){
27401             this.setThumbBoxSize();
27402         }
27403         
27404         this.setThumbBoxPosition();
27405         
27406         this.baseScaleLevel();
27407         
27408         this.draw();
27409         
27410         this.resize();
27411         
27412         this.canvasLoaded = true;
27413         
27414         if(this.loadMask){
27415             this.maskEl.unmask();
27416         }
27417         
27418     },
27419     
27420     setCanvasPosition : function()
27421     {   
27422         if(!this.canvasEl){
27423             return;
27424         }
27425         
27426         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27427         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27428         
27429         this.previewEl.setLeft(pw);
27430         this.previewEl.setTop(ph);
27431         
27432     },
27433     
27434     onMouseDown : function(e)
27435     {   
27436         e.stopEvent();
27437         
27438         this.dragable = true;
27439         this.pinching = false;
27440         
27441         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27442             this.dragable = false;
27443             return;
27444         }
27445         
27446         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27447         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27448         
27449     },
27450     
27451     onMouseMove : function(e)
27452     {   
27453         e.stopEvent();
27454         
27455         if(!this.canvasLoaded){
27456             return;
27457         }
27458         
27459         if (!this.dragable){
27460             return;
27461         }
27462         
27463         var minX = Math.ceil(this.thumbEl.getLeft(true));
27464         var minY = Math.ceil(this.thumbEl.getTop(true));
27465         
27466         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27467         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27468         
27469         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27470         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27471         
27472         x = x - this.mouseX;
27473         y = y - this.mouseY;
27474         
27475         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27476         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27477         
27478         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27479         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27480         
27481         this.previewEl.setLeft(bgX);
27482         this.previewEl.setTop(bgY);
27483         
27484         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27485         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27486     },
27487     
27488     onMouseUp : function(e)
27489     {   
27490         e.stopEvent();
27491         
27492         this.dragable = false;
27493     },
27494     
27495     onMouseWheel : function(e)
27496     {   
27497         e.stopEvent();
27498         
27499         this.startScale = this.scale;
27500         
27501         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27502         
27503         if(!this.zoomable()){
27504             this.scale = this.startScale;
27505             return;
27506         }
27507         
27508         this.draw();
27509         
27510         return;
27511     },
27512     
27513     zoomable : function()
27514     {
27515         var minScale = this.thumbEl.getWidth() / this.minWidth;
27516         
27517         if(this.minWidth < this.minHeight){
27518             minScale = this.thumbEl.getHeight() / this.minHeight;
27519         }
27520         
27521         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27522         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27523         
27524         if(
27525                 this.isDocument &&
27526                 (this.rotate == 0 || this.rotate == 180) && 
27527                 (
27528                     width > this.imageEl.OriginWidth || 
27529                     height > this.imageEl.OriginHeight ||
27530                     (width < this.minWidth && height < this.minHeight)
27531                 )
27532         ){
27533             return false;
27534         }
27535         
27536         if(
27537                 this.isDocument &&
27538                 (this.rotate == 90 || this.rotate == 270) && 
27539                 (
27540                     width > this.imageEl.OriginWidth || 
27541                     height > this.imageEl.OriginHeight ||
27542                     (width < this.minHeight && height < this.minWidth)
27543                 )
27544         ){
27545             return false;
27546         }
27547         
27548         if(
27549                 !this.isDocument &&
27550                 (this.rotate == 0 || this.rotate == 180) && 
27551                 (
27552                     width < this.minWidth || 
27553                     width > this.imageEl.OriginWidth || 
27554                     height < this.minHeight || 
27555                     height > this.imageEl.OriginHeight
27556                 )
27557         ){
27558             return false;
27559         }
27560         
27561         if(
27562                 !this.isDocument &&
27563                 (this.rotate == 90 || this.rotate == 270) && 
27564                 (
27565                     width < this.minHeight || 
27566                     width > this.imageEl.OriginWidth || 
27567                     height < this.minWidth || 
27568                     height > this.imageEl.OriginHeight
27569                 )
27570         ){
27571             return false;
27572         }
27573         
27574         return true;
27575         
27576     },
27577     
27578     onRotateLeft : function(e)
27579     {   
27580         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27581             
27582             var minScale = this.thumbEl.getWidth() / this.minWidth;
27583             
27584             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27585             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27586             
27587             this.startScale = this.scale;
27588             
27589             while (this.getScaleLevel() < minScale){
27590             
27591                 this.scale = this.scale + 1;
27592                 
27593                 if(!this.zoomable()){
27594                     break;
27595                 }
27596                 
27597                 if(
27598                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27599                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27600                 ){
27601                     continue;
27602                 }
27603                 
27604                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27605
27606                 this.draw();
27607                 
27608                 return;
27609             }
27610             
27611             this.scale = this.startScale;
27612             
27613             this.onRotateFail();
27614             
27615             return false;
27616         }
27617         
27618         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27619
27620         if(this.isDocument){
27621             this.setThumbBoxSize();
27622             this.setThumbBoxPosition();
27623             this.setCanvasPosition();
27624         }
27625         
27626         this.draw();
27627         
27628         this.fireEvent('rotate', this, 'left');
27629         
27630     },
27631     
27632     onRotateRight : function(e)
27633     {
27634         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27635             
27636             var minScale = this.thumbEl.getWidth() / this.minWidth;
27637         
27638             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27639             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27640             
27641             this.startScale = this.scale;
27642             
27643             while (this.getScaleLevel() < minScale){
27644             
27645                 this.scale = this.scale + 1;
27646                 
27647                 if(!this.zoomable()){
27648                     break;
27649                 }
27650                 
27651                 if(
27652                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27653                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27654                 ){
27655                     continue;
27656                 }
27657                 
27658                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27659
27660                 this.draw();
27661                 
27662                 return;
27663             }
27664             
27665             this.scale = this.startScale;
27666             
27667             this.onRotateFail();
27668             
27669             return false;
27670         }
27671         
27672         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27673
27674         if(this.isDocument){
27675             this.setThumbBoxSize();
27676             this.setThumbBoxPosition();
27677             this.setCanvasPosition();
27678         }
27679         
27680         this.draw();
27681         
27682         this.fireEvent('rotate', this, 'right');
27683     },
27684     
27685     onRotateFail : function()
27686     {
27687         this.errorEl.show(true);
27688         
27689         var _this = this;
27690         
27691         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27692     },
27693     
27694     draw : function()
27695     {
27696         this.previewEl.dom.innerHTML = '';
27697         
27698         var canvasEl = document.createElement("canvas");
27699         
27700         var contextEl = canvasEl.getContext("2d");
27701         
27702         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27703         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27704         var center = this.imageEl.OriginWidth / 2;
27705         
27706         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27707             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27708             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27709             center = this.imageEl.OriginHeight / 2;
27710         }
27711         
27712         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27713         
27714         contextEl.translate(center, center);
27715         contextEl.rotate(this.rotate * Math.PI / 180);
27716
27717         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27718         
27719         this.canvasEl = document.createElement("canvas");
27720         
27721         this.contextEl = this.canvasEl.getContext("2d");
27722         
27723         switch (this.rotate) {
27724             case 0 :
27725                 
27726                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27727                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27728                 
27729                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27730                 
27731                 break;
27732             case 90 : 
27733                 
27734                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27735                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27736                 
27737                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27738                     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);
27739                     break;
27740                 }
27741                 
27742                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27743                 
27744                 break;
27745             case 180 :
27746                 
27747                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27748                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27749                 
27750                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27751                     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);
27752                     break;
27753                 }
27754                 
27755                 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);
27756                 
27757                 break;
27758             case 270 :
27759                 
27760                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27761                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27762         
27763                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27764                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27765                     break;
27766                 }
27767                 
27768                 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);
27769                 
27770                 break;
27771             default : 
27772                 break;
27773         }
27774         
27775         this.previewEl.appendChild(this.canvasEl);
27776         
27777         this.setCanvasPosition();
27778     },
27779     
27780     crop : function()
27781     {
27782         if(!this.canvasLoaded){
27783             return;
27784         }
27785         
27786         var imageCanvas = document.createElement("canvas");
27787         
27788         var imageContext = imageCanvas.getContext("2d");
27789         
27790         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27791         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27792         
27793         var center = imageCanvas.width / 2;
27794         
27795         imageContext.translate(center, center);
27796         
27797         imageContext.rotate(this.rotate * Math.PI / 180);
27798         
27799         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27800         
27801         var canvas = document.createElement("canvas");
27802         
27803         var context = canvas.getContext("2d");
27804                 
27805         canvas.width = this.minWidth;
27806         canvas.height = this.minHeight;
27807
27808         switch (this.rotate) {
27809             case 0 :
27810                 
27811                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27812                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27813                 
27814                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27815                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27816                 
27817                 var targetWidth = this.minWidth - 2 * x;
27818                 var targetHeight = this.minHeight - 2 * y;
27819                 
27820                 var scale = 1;
27821                 
27822                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27823                     scale = targetWidth / width;
27824                 }
27825                 
27826                 if(x > 0 && y == 0){
27827                     scale = targetHeight / height;
27828                 }
27829                 
27830                 if(x > 0 && y > 0){
27831                     scale = targetWidth / width;
27832                     
27833                     if(width < height){
27834                         scale = targetHeight / height;
27835                     }
27836                 }
27837                 
27838                 context.scale(scale, scale);
27839                 
27840                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27841                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27842
27843                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27844                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27845
27846                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27847                 
27848                 break;
27849             case 90 : 
27850                 
27851                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27852                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27853                 
27854                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27855                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27856                 
27857                 var targetWidth = this.minWidth - 2 * x;
27858                 var targetHeight = this.minHeight - 2 * y;
27859                 
27860                 var scale = 1;
27861                 
27862                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27863                     scale = targetWidth / width;
27864                 }
27865                 
27866                 if(x > 0 && y == 0){
27867                     scale = targetHeight / height;
27868                 }
27869                 
27870                 if(x > 0 && y > 0){
27871                     scale = targetWidth / width;
27872                     
27873                     if(width < height){
27874                         scale = targetHeight / height;
27875                     }
27876                 }
27877                 
27878                 context.scale(scale, scale);
27879                 
27880                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27881                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27882
27883                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27884                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27885                 
27886                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27887                 
27888                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27889                 
27890                 break;
27891             case 180 :
27892                 
27893                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27894                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27895                 
27896                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27897                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27898                 
27899                 var targetWidth = this.minWidth - 2 * x;
27900                 var targetHeight = this.minHeight - 2 * y;
27901                 
27902                 var scale = 1;
27903                 
27904                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27905                     scale = targetWidth / width;
27906                 }
27907                 
27908                 if(x > 0 && y == 0){
27909                     scale = targetHeight / height;
27910                 }
27911                 
27912                 if(x > 0 && y > 0){
27913                     scale = targetWidth / width;
27914                     
27915                     if(width < height){
27916                         scale = targetHeight / height;
27917                     }
27918                 }
27919                 
27920                 context.scale(scale, scale);
27921                 
27922                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27923                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27924
27925                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27926                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27927
27928                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27929                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27930                 
27931                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27932                 
27933                 break;
27934             case 270 :
27935                 
27936                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27937                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27938                 
27939                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27940                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27941                 
27942                 var targetWidth = this.minWidth - 2 * x;
27943                 var targetHeight = this.minHeight - 2 * y;
27944                 
27945                 var scale = 1;
27946                 
27947                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27948                     scale = targetWidth / width;
27949                 }
27950                 
27951                 if(x > 0 && y == 0){
27952                     scale = targetHeight / height;
27953                 }
27954                 
27955                 if(x > 0 && y > 0){
27956                     scale = targetWidth / width;
27957                     
27958                     if(width < height){
27959                         scale = targetHeight / height;
27960                     }
27961                 }
27962                 
27963                 context.scale(scale, scale);
27964                 
27965                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27966                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27967
27968                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27969                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27970                 
27971                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27972                 
27973                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27974                 
27975                 break;
27976             default : 
27977                 break;
27978         }
27979         
27980         this.cropData = canvas.toDataURL(this.cropType);
27981         
27982         if(this.fireEvent('crop', this, this.cropData) !== false){
27983             this.process(this.file, this.cropData);
27984         }
27985         
27986         return;
27987         
27988     },
27989     
27990     setThumbBoxSize : function()
27991     {
27992         var width, height;
27993         
27994         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27995             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27996             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27997             
27998             this.minWidth = width;
27999             this.minHeight = height;
28000             
28001             if(this.rotate == 90 || this.rotate == 270){
28002                 this.minWidth = height;
28003                 this.minHeight = width;
28004             }
28005         }
28006         
28007         height = 300;
28008         width = Math.ceil(this.minWidth * height / this.minHeight);
28009         
28010         if(this.minWidth > this.minHeight){
28011             width = 300;
28012             height = Math.ceil(this.minHeight * width / this.minWidth);
28013         }
28014         
28015         this.thumbEl.setStyle({
28016             width : width + 'px',
28017             height : height + 'px'
28018         });
28019
28020         return;
28021             
28022     },
28023     
28024     setThumbBoxPosition : function()
28025     {
28026         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28027         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28028         
28029         this.thumbEl.setLeft(x);
28030         this.thumbEl.setTop(y);
28031         
28032     },
28033     
28034     baseRotateLevel : function()
28035     {
28036         this.baseRotate = 1;
28037         
28038         if(
28039                 typeof(this.exif) != 'undefined' &&
28040                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28041                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28042         ){
28043             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28044         }
28045         
28046         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28047         
28048     },
28049     
28050     baseScaleLevel : function()
28051     {
28052         var width, height;
28053         
28054         if(this.isDocument){
28055             
28056             if(this.baseRotate == 6 || this.baseRotate == 8){
28057             
28058                 height = this.thumbEl.getHeight();
28059                 this.baseScale = height / this.imageEl.OriginWidth;
28060
28061                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28062                     width = this.thumbEl.getWidth();
28063                     this.baseScale = width / this.imageEl.OriginHeight;
28064                 }
28065
28066                 return;
28067             }
28068
28069             height = this.thumbEl.getHeight();
28070             this.baseScale = height / this.imageEl.OriginHeight;
28071
28072             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28073                 width = this.thumbEl.getWidth();
28074                 this.baseScale = width / this.imageEl.OriginWidth;
28075             }
28076
28077             return;
28078         }
28079         
28080         if(this.baseRotate == 6 || this.baseRotate == 8){
28081             
28082             width = this.thumbEl.getHeight();
28083             this.baseScale = width / this.imageEl.OriginHeight;
28084             
28085             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28086                 height = this.thumbEl.getWidth();
28087                 this.baseScale = height / this.imageEl.OriginHeight;
28088             }
28089             
28090             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28091                 height = this.thumbEl.getWidth();
28092                 this.baseScale = height / this.imageEl.OriginHeight;
28093                 
28094                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28095                     width = this.thumbEl.getHeight();
28096                     this.baseScale = width / this.imageEl.OriginWidth;
28097                 }
28098             }
28099             
28100             return;
28101         }
28102         
28103         width = this.thumbEl.getWidth();
28104         this.baseScale = width / this.imageEl.OriginWidth;
28105         
28106         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28107             height = this.thumbEl.getHeight();
28108             this.baseScale = height / this.imageEl.OriginHeight;
28109         }
28110         
28111         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28112             
28113             height = this.thumbEl.getHeight();
28114             this.baseScale = height / this.imageEl.OriginHeight;
28115             
28116             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28117                 width = this.thumbEl.getWidth();
28118                 this.baseScale = width / this.imageEl.OriginWidth;
28119             }
28120             
28121         }
28122         
28123         return;
28124     },
28125     
28126     getScaleLevel : function()
28127     {
28128         return this.baseScale * Math.pow(1.1, this.scale);
28129     },
28130     
28131     onTouchStart : function(e)
28132     {
28133         if(!this.canvasLoaded){
28134             this.beforeSelectFile(e);
28135             return;
28136         }
28137         
28138         var touches = e.browserEvent.touches;
28139         
28140         if(!touches){
28141             return;
28142         }
28143         
28144         if(touches.length == 1){
28145             this.onMouseDown(e);
28146             return;
28147         }
28148         
28149         if(touches.length != 2){
28150             return;
28151         }
28152         
28153         var coords = [];
28154         
28155         for(var i = 0, finger; finger = touches[i]; i++){
28156             coords.push(finger.pageX, finger.pageY);
28157         }
28158         
28159         var x = Math.pow(coords[0] - coords[2], 2);
28160         var y = Math.pow(coords[1] - coords[3], 2);
28161         
28162         this.startDistance = Math.sqrt(x + y);
28163         
28164         this.startScale = this.scale;
28165         
28166         this.pinching = true;
28167         this.dragable = false;
28168         
28169     },
28170     
28171     onTouchMove : function(e)
28172     {
28173         if(!this.pinching && !this.dragable){
28174             return;
28175         }
28176         
28177         var touches = e.browserEvent.touches;
28178         
28179         if(!touches){
28180             return;
28181         }
28182         
28183         if(this.dragable){
28184             this.onMouseMove(e);
28185             return;
28186         }
28187         
28188         var coords = [];
28189         
28190         for(var i = 0, finger; finger = touches[i]; i++){
28191             coords.push(finger.pageX, finger.pageY);
28192         }
28193         
28194         var x = Math.pow(coords[0] - coords[2], 2);
28195         var y = Math.pow(coords[1] - coords[3], 2);
28196         
28197         this.endDistance = Math.sqrt(x + y);
28198         
28199         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28200         
28201         if(!this.zoomable()){
28202             this.scale = this.startScale;
28203             return;
28204         }
28205         
28206         this.draw();
28207         
28208     },
28209     
28210     onTouchEnd : function(e)
28211     {
28212         this.pinching = false;
28213         this.dragable = false;
28214         
28215     },
28216     
28217     process : function(file, crop)
28218     {
28219         if(this.loadMask){
28220             this.maskEl.mask(this.loadingText);
28221         }
28222         
28223         this.xhr = new XMLHttpRequest();
28224         
28225         file.xhr = this.xhr;
28226
28227         this.xhr.open(this.method, this.url, true);
28228         
28229         var headers = {
28230             "Accept": "application/json",
28231             "Cache-Control": "no-cache",
28232             "X-Requested-With": "XMLHttpRequest"
28233         };
28234         
28235         for (var headerName in headers) {
28236             var headerValue = headers[headerName];
28237             if (headerValue) {
28238                 this.xhr.setRequestHeader(headerName, headerValue);
28239             }
28240         }
28241         
28242         var _this = this;
28243         
28244         this.xhr.onload = function()
28245         {
28246             _this.xhrOnLoad(_this.xhr);
28247         }
28248         
28249         this.xhr.onerror = function()
28250         {
28251             _this.xhrOnError(_this.xhr);
28252         }
28253         
28254         var formData = new FormData();
28255
28256         formData.append('returnHTML', 'NO');
28257         
28258         if(crop){
28259             formData.append('crop', crop);
28260         }
28261         
28262         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28263             formData.append(this.paramName, file, file.name);
28264         }
28265         
28266         if(typeof(file.filename) != 'undefined'){
28267             formData.append('filename', file.filename);
28268         }
28269         
28270         if(typeof(file.mimetype) != 'undefined'){
28271             formData.append('mimetype', file.mimetype);
28272         }
28273         
28274         if(this.fireEvent('arrange', this, formData) != false){
28275             this.xhr.send(formData);
28276         };
28277     },
28278     
28279     xhrOnLoad : function(xhr)
28280     {
28281         if(this.loadMask){
28282             this.maskEl.unmask();
28283         }
28284         
28285         if (xhr.readyState !== 4) {
28286             this.fireEvent('exception', this, xhr);
28287             return;
28288         }
28289
28290         var response = Roo.decode(xhr.responseText);
28291         
28292         if(!response.success){
28293             this.fireEvent('exception', this, xhr);
28294             return;
28295         }
28296         
28297         var response = Roo.decode(xhr.responseText);
28298         
28299         this.fireEvent('upload', this, response);
28300         
28301     },
28302     
28303     xhrOnError : function()
28304     {
28305         if(this.loadMask){
28306             this.maskEl.unmask();
28307         }
28308         
28309         Roo.log('xhr on error');
28310         
28311         var response = Roo.decode(xhr.responseText);
28312           
28313         Roo.log(response);
28314         
28315     },
28316     
28317     prepare : function(file)
28318     {   
28319         if(this.loadMask){
28320             this.maskEl.mask(this.loadingText);
28321         }
28322         
28323         this.file = false;
28324         this.exif = {};
28325         
28326         if(typeof(file) === 'string'){
28327             this.loadCanvas(file);
28328             return;
28329         }
28330         
28331         if(!file || !this.urlAPI){
28332             return;
28333         }
28334         
28335         this.file = file;
28336         this.cropType = file.type;
28337         
28338         var _this = this;
28339         
28340         if(this.fireEvent('prepare', this, this.file) != false){
28341             
28342             var reader = new FileReader();
28343             
28344             reader.onload = function (e) {
28345                 if (e.target.error) {
28346                     Roo.log(e.target.error);
28347                     return;
28348                 }
28349                 
28350                 var buffer = e.target.result,
28351                     dataView = new DataView(buffer),
28352                     offset = 2,
28353                     maxOffset = dataView.byteLength - 4,
28354                     markerBytes,
28355                     markerLength;
28356                 
28357                 if (dataView.getUint16(0) === 0xffd8) {
28358                     while (offset < maxOffset) {
28359                         markerBytes = dataView.getUint16(offset);
28360                         
28361                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28362                             markerLength = dataView.getUint16(offset + 2) + 2;
28363                             if (offset + markerLength > dataView.byteLength) {
28364                                 Roo.log('Invalid meta data: Invalid segment size.');
28365                                 break;
28366                             }
28367                             
28368                             if(markerBytes == 0xffe1){
28369                                 _this.parseExifData(
28370                                     dataView,
28371                                     offset,
28372                                     markerLength
28373                                 );
28374                             }
28375                             
28376                             offset += markerLength;
28377                             
28378                             continue;
28379                         }
28380                         
28381                         break;
28382                     }
28383                     
28384                 }
28385                 
28386                 var url = _this.urlAPI.createObjectURL(_this.file);
28387                 
28388                 _this.loadCanvas(url);
28389                 
28390                 return;
28391             }
28392             
28393             reader.readAsArrayBuffer(this.file);
28394             
28395         }
28396         
28397     },
28398     
28399     parseExifData : function(dataView, offset, length)
28400     {
28401         var tiffOffset = offset + 10,
28402             littleEndian,
28403             dirOffset;
28404     
28405         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28406             // No Exif data, might be XMP data instead
28407             return;
28408         }
28409         
28410         // Check for the ASCII code for "Exif" (0x45786966):
28411         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28412             // No Exif data, might be XMP data instead
28413             return;
28414         }
28415         if (tiffOffset + 8 > dataView.byteLength) {
28416             Roo.log('Invalid Exif data: Invalid segment size.');
28417             return;
28418         }
28419         // Check for the two null bytes:
28420         if (dataView.getUint16(offset + 8) !== 0x0000) {
28421             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28422             return;
28423         }
28424         // Check the byte alignment:
28425         switch (dataView.getUint16(tiffOffset)) {
28426         case 0x4949:
28427             littleEndian = true;
28428             break;
28429         case 0x4D4D:
28430             littleEndian = false;
28431             break;
28432         default:
28433             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28434             return;
28435         }
28436         // Check for the TIFF tag marker (0x002A):
28437         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28438             Roo.log('Invalid Exif data: Missing TIFF marker.');
28439             return;
28440         }
28441         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28442         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28443         
28444         this.parseExifTags(
28445             dataView,
28446             tiffOffset,
28447             tiffOffset + dirOffset,
28448             littleEndian
28449         );
28450     },
28451     
28452     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28453     {
28454         var tagsNumber,
28455             dirEndOffset,
28456             i;
28457         if (dirOffset + 6 > dataView.byteLength) {
28458             Roo.log('Invalid Exif data: Invalid directory offset.');
28459             return;
28460         }
28461         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28462         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28463         if (dirEndOffset + 4 > dataView.byteLength) {
28464             Roo.log('Invalid Exif data: Invalid directory size.');
28465             return;
28466         }
28467         for (i = 0; i < tagsNumber; i += 1) {
28468             this.parseExifTag(
28469                 dataView,
28470                 tiffOffset,
28471                 dirOffset + 2 + 12 * i, // tag offset
28472                 littleEndian
28473             );
28474         }
28475         // Return the offset to the next directory:
28476         return dataView.getUint32(dirEndOffset, littleEndian);
28477     },
28478     
28479     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28480     {
28481         var tag = dataView.getUint16(offset, littleEndian);
28482         
28483         this.exif[tag] = this.getExifValue(
28484             dataView,
28485             tiffOffset,
28486             offset,
28487             dataView.getUint16(offset + 2, littleEndian), // tag type
28488             dataView.getUint32(offset + 4, littleEndian), // tag length
28489             littleEndian
28490         );
28491     },
28492     
28493     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28494     {
28495         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28496             tagSize,
28497             dataOffset,
28498             values,
28499             i,
28500             str,
28501             c;
28502     
28503         if (!tagType) {
28504             Roo.log('Invalid Exif data: Invalid tag type.');
28505             return;
28506         }
28507         
28508         tagSize = tagType.size * length;
28509         // Determine if the value is contained in the dataOffset bytes,
28510         // or if the value at the dataOffset is a pointer to the actual data:
28511         dataOffset = tagSize > 4 ?
28512                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28513         if (dataOffset + tagSize > dataView.byteLength) {
28514             Roo.log('Invalid Exif data: Invalid data offset.');
28515             return;
28516         }
28517         if (length === 1) {
28518             return tagType.getValue(dataView, dataOffset, littleEndian);
28519         }
28520         values = [];
28521         for (i = 0; i < length; i += 1) {
28522             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28523         }
28524         
28525         if (tagType.ascii) {
28526             str = '';
28527             // Concatenate the chars:
28528             for (i = 0; i < values.length; i += 1) {
28529                 c = values[i];
28530                 // Ignore the terminating NULL byte(s):
28531                 if (c === '\u0000') {
28532                     break;
28533                 }
28534                 str += c;
28535             }
28536             return str;
28537         }
28538         return values;
28539     }
28540     
28541 });
28542
28543 Roo.apply(Roo.bootstrap.UploadCropbox, {
28544     tags : {
28545         'Orientation': 0x0112
28546     },
28547     
28548     Orientation: {
28549             1: 0, //'top-left',
28550 //            2: 'top-right',
28551             3: 180, //'bottom-right',
28552 //            4: 'bottom-left',
28553 //            5: 'left-top',
28554             6: 90, //'right-top',
28555 //            7: 'right-bottom',
28556             8: 270 //'left-bottom'
28557     },
28558     
28559     exifTagTypes : {
28560         // byte, 8-bit unsigned int:
28561         1: {
28562             getValue: function (dataView, dataOffset) {
28563                 return dataView.getUint8(dataOffset);
28564             },
28565             size: 1
28566         },
28567         // ascii, 8-bit byte:
28568         2: {
28569             getValue: function (dataView, dataOffset) {
28570                 return String.fromCharCode(dataView.getUint8(dataOffset));
28571             },
28572             size: 1,
28573             ascii: true
28574         },
28575         // short, 16 bit int:
28576         3: {
28577             getValue: function (dataView, dataOffset, littleEndian) {
28578                 return dataView.getUint16(dataOffset, littleEndian);
28579             },
28580             size: 2
28581         },
28582         // long, 32 bit int:
28583         4: {
28584             getValue: function (dataView, dataOffset, littleEndian) {
28585                 return dataView.getUint32(dataOffset, littleEndian);
28586             },
28587             size: 4
28588         },
28589         // rational = two long values, first is numerator, second is denominator:
28590         5: {
28591             getValue: function (dataView, dataOffset, littleEndian) {
28592                 return dataView.getUint32(dataOffset, littleEndian) /
28593                     dataView.getUint32(dataOffset + 4, littleEndian);
28594             },
28595             size: 8
28596         },
28597         // slong, 32 bit signed int:
28598         9: {
28599             getValue: function (dataView, dataOffset, littleEndian) {
28600                 return dataView.getInt32(dataOffset, littleEndian);
28601             },
28602             size: 4
28603         },
28604         // srational, two slongs, first is numerator, second is denominator:
28605         10: {
28606             getValue: function (dataView, dataOffset, littleEndian) {
28607                 return dataView.getInt32(dataOffset, littleEndian) /
28608                     dataView.getInt32(dataOffset + 4, littleEndian);
28609             },
28610             size: 8
28611         }
28612     },
28613     
28614     footer : {
28615         STANDARD : [
28616             {
28617                 tag : 'div',
28618                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28619                 action : 'rotate-left',
28620                 cn : [
28621                     {
28622                         tag : 'button',
28623                         cls : 'btn btn-default',
28624                         html : '<i class="fa fa-undo"></i>'
28625                     }
28626                 ]
28627             },
28628             {
28629                 tag : 'div',
28630                 cls : 'btn-group roo-upload-cropbox-picture',
28631                 action : 'picture',
28632                 cn : [
28633                     {
28634                         tag : 'button',
28635                         cls : 'btn btn-default',
28636                         html : '<i class="fa fa-picture-o"></i>'
28637                     }
28638                 ]
28639             },
28640             {
28641                 tag : 'div',
28642                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28643                 action : 'rotate-right',
28644                 cn : [
28645                     {
28646                         tag : 'button',
28647                         cls : 'btn btn-default',
28648                         html : '<i class="fa fa-repeat"></i>'
28649                     }
28650                 ]
28651             }
28652         ],
28653         DOCUMENT : [
28654             {
28655                 tag : 'div',
28656                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28657                 action : 'rotate-left',
28658                 cn : [
28659                     {
28660                         tag : 'button',
28661                         cls : 'btn btn-default',
28662                         html : '<i class="fa fa-undo"></i>'
28663                     }
28664                 ]
28665             },
28666             {
28667                 tag : 'div',
28668                 cls : 'btn-group roo-upload-cropbox-download',
28669                 action : 'download',
28670                 cn : [
28671                     {
28672                         tag : 'button',
28673                         cls : 'btn btn-default',
28674                         html : '<i class="fa fa-download"></i>'
28675                     }
28676                 ]
28677             },
28678             {
28679                 tag : 'div',
28680                 cls : 'btn-group roo-upload-cropbox-crop',
28681                 action : 'crop',
28682                 cn : [
28683                     {
28684                         tag : 'button',
28685                         cls : 'btn btn-default',
28686                         html : '<i class="fa fa-crop"></i>'
28687                     }
28688                 ]
28689             },
28690             {
28691                 tag : 'div',
28692                 cls : 'btn-group roo-upload-cropbox-trash',
28693                 action : 'trash',
28694                 cn : [
28695                     {
28696                         tag : 'button',
28697                         cls : 'btn btn-default',
28698                         html : '<i class="fa fa-trash"></i>'
28699                     }
28700                 ]
28701             },
28702             {
28703                 tag : 'div',
28704                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28705                 action : 'rotate-right',
28706                 cn : [
28707                     {
28708                         tag : 'button',
28709                         cls : 'btn btn-default',
28710                         html : '<i class="fa fa-repeat"></i>'
28711                     }
28712                 ]
28713             }
28714         ],
28715         ROTATOR : [
28716             {
28717                 tag : 'div',
28718                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28719                 action : 'rotate-left',
28720                 cn : [
28721                     {
28722                         tag : 'button',
28723                         cls : 'btn btn-default',
28724                         html : '<i class="fa fa-undo"></i>'
28725                     }
28726                 ]
28727             },
28728             {
28729                 tag : 'div',
28730                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28731                 action : 'rotate-right',
28732                 cn : [
28733                     {
28734                         tag : 'button',
28735                         cls : 'btn btn-default',
28736                         html : '<i class="fa fa-repeat"></i>'
28737                     }
28738                 ]
28739             }
28740         ]
28741     }
28742 });
28743
28744 /*
28745 * Licence: LGPL
28746 */
28747
28748 /**
28749  * @class Roo.bootstrap.DocumentManager
28750  * @extends Roo.bootstrap.Component
28751  * Bootstrap DocumentManager class
28752  * @cfg {String} paramName default 'imageUpload'
28753  * @cfg {String} toolTipName default 'filename'
28754  * @cfg {String} method default POST
28755  * @cfg {String} url action url
28756  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28757  * @cfg {Boolean} multiple multiple upload default true
28758  * @cfg {Number} thumbSize default 300
28759  * @cfg {String} fieldLabel
28760  * @cfg {Number} labelWidth default 4
28761  * @cfg {String} labelAlign (left|top) default left
28762  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28763 * @cfg {Number} labellg set the width of label (1-12)
28764  * @cfg {Number} labelmd set the width of label (1-12)
28765  * @cfg {Number} labelsm set the width of label (1-12)
28766  * @cfg {Number} labelxs set the width of label (1-12)
28767  * 
28768  * @constructor
28769  * Create a new DocumentManager
28770  * @param {Object} config The config object
28771  */
28772
28773 Roo.bootstrap.DocumentManager = function(config){
28774     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28775     
28776     this.files = [];
28777     this.delegates = [];
28778     
28779     this.addEvents({
28780         /**
28781          * @event initial
28782          * Fire when initial the DocumentManager
28783          * @param {Roo.bootstrap.DocumentManager} this
28784          */
28785         "initial" : true,
28786         /**
28787          * @event inspect
28788          * inspect selected file
28789          * @param {Roo.bootstrap.DocumentManager} this
28790          * @param {File} file
28791          */
28792         "inspect" : true,
28793         /**
28794          * @event exception
28795          * Fire when xhr load exception
28796          * @param {Roo.bootstrap.DocumentManager} this
28797          * @param {XMLHttpRequest} xhr
28798          */
28799         "exception" : true,
28800         /**
28801          * @event afterupload
28802          * Fire when xhr load exception
28803          * @param {Roo.bootstrap.DocumentManager} this
28804          * @param {XMLHttpRequest} xhr
28805          */
28806         "afterupload" : true,
28807         /**
28808          * @event prepare
28809          * prepare the form data
28810          * @param {Roo.bootstrap.DocumentManager} this
28811          * @param {Object} formData
28812          */
28813         "prepare" : true,
28814         /**
28815          * @event remove
28816          * Fire when remove the file
28817          * @param {Roo.bootstrap.DocumentManager} this
28818          * @param {Object} file
28819          */
28820         "remove" : true,
28821         /**
28822          * @event refresh
28823          * Fire after refresh the file
28824          * @param {Roo.bootstrap.DocumentManager} this
28825          */
28826         "refresh" : true,
28827         /**
28828          * @event click
28829          * Fire after click the image
28830          * @param {Roo.bootstrap.DocumentManager} this
28831          * @param {Object} file
28832          */
28833         "click" : true,
28834         /**
28835          * @event edit
28836          * Fire when upload a image and editable set to true
28837          * @param {Roo.bootstrap.DocumentManager} this
28838          * @param {Object} file
28839          */
28840         "edit" : true,
28841         /**
28842          * @event beforeselectfile
28843          * Fire before select file
28844          * @param {Roo.bootstrap.DocumentManager} this
28845          */
28846         "beforeselectfile" : true,
28847         /**
28848          * @event process
28849          * Fire before process file
28850          * @param {Roo.bootstrap.DocumentManager} this
28851          * @param {Object} file
28852          */
28853         "process" : true,
28854         /**
28855          * @event previewrendered
28856          * Fire when preview rendered
28857          * @param {Roo.bootstrap.DocumentManager} this
28858          * @param {Object} file
28859          */
28860         "previewrendered" : true,
28861         /**
28862          */
28863         "previewResize" : true
28864         
28865     });
28866 };
28867
28868 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28869     
28870     boxes : 0,
28871     inputName : '',
28872     thumbSize : 300,
28873     multiple : true,
28874     files : false,
28875     method : 'POST',
28876     url : '',
28877     paramName : 'imageUpload',
28878     toolTipName : 'filename',
28879     fieldLabel : '',
28880     labelWidth : 4,
28881     labelAlign : 'left',
28882     editable : true,
28883     delegates : false,
28884     xhr : false, 
28885     
28886     labellg : 0,
28887     labelmd : 0,
28888     labelsm : 0,
28889     labelxs : 0,
28890     
28891     getAutoCreate : function()
28892     {   
28893         var managerWidget = {
28894             tag : 'div',
28895             cls : 'roo-document-manager',
28896             cn : [
28897                 {
28898                     tag : 'input',
28899                     cls : 'roo-document-manager-selector',
28900                     type : 'file'
28901                 },
28902                 {
28903                     tag : 'div',
28904                     cls : 'roo-document-manager-uploader',
28905                     cn : [
28906                         {
28907                             tag : 'div',
28908                             cls : 'roo-document-manager-upload-btn',
28909                             html : '<i class="fa fa-plus"></i>'
28910                         }
28911                     ]
28912                     
28913                 }
28914             ]
28915         };
28916         
28917         var content = [
28918             {
28919                 tag : 'div',
28920                 cls : 'column col-md-12',
28921                 cn : managerWidget
28922             }
28923         ];
28924         
28925         if(this.fieldLabel.length){
28926             
28927             content = [
28928                 {
28929                     tag : 'div',
28930                     cls : 'column col-md-12',
28931                     html : this.fieldLabel
28932                 },
28933                 {
28934                     tag : 'div',
28935                     cls : 'column col-md-12',
28936                     cn : managerWidget
28937                 }
28938             ];
28939
28940             if(this.labelAlign == 'left'){
28941                 content = [
28942                     {
28943                         tag : 'div',
28944                         cls : 'column',
28945                         html : this.fieldLabel
28946                     },
28947                     {
28948                         tag : 'div',
28949                         cls : 'column',
28950                         cn : managerWidget
28951                     }
28952                 ];
28953                 
28954                 if(this.labelWidth > 12){
28955                     content[0].style = "width: " + this.labelWidth + 'px';
28956                 }
28957
28958                 if(this.labelWidth < 13 && this.labelmd == 0){
28959                     this.labelmd = this.labelWidth;
28960                 }
28961
28962                 if(this.labellg > 0){
28963                     content[0].cls += ' col-lg-' + this.labellg;
28964                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28965                 }
28966
28967                 if(this.labelmd > 0){
28968                     content[0].cls += ' col-md-' + this.labelmd;
28969                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28970                 }
28971
28972                 if(this.labelsm > 0){
28973                     content[0].cls += ' col-sm-' + this.labelsm;
28974                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28975                 }
28976
28977                 if(this.labelxs > 0){
28978                     content[0].cls += ' col-xs-' + this.labelxs;
28979                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28980                 }
28981                 
28982             }
28983         }
28984         
28985         var cfg = {
28986             tag : 'div',
28987             cls : 'row clearfix',
28988             cn : content
28989         };
28990         
28991         return cfg;
28992         
28993     },
28994     
28995     initEvents : function()
28996     {
28997         this.managerEl = this.el.select('.roo-document-manager', true).first();
28998         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28999         
29000         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29001         this.selectorEl.hide();
29002         
29003         if(this.multiple){
29004             this.selectorEl.attr('multiple', 'multiple');
29005         }
29006         
29007         this.selectorEl.on('change', this.onFileSelected, this);
29008         
29009         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29010         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29011         
29012         this.uploader.on('click', this.onUploaderClick, this);
29013         
29014         this.renderProgressDialog();
29015         
29016         var _this = this;
29017         
29018         window.addEventListener("resize", function() { _this.refresh(); } );
29019         
29020         this.fireEvent('initial', this);
29021     },
29022     
29023     renderProgressDialog : function()
29024     {
29025         var _this = this;
29026         
29027         this.progressDialog = new Roo.bootstrap.Modal({
29028             cls : 'roo-document-manager-progress-dialog',
29029             allow_close : false,
29030             title : '',
29031             buttons : [
29032                 {
29033                     name  :'cancel',
29034                     weight : 'danger',
29035                     html : 'Cancel'
29036                 }
29037             ], 
29038             listeners : { 
29039                 btnclick : function() {
29040                     _this.uploadCancel();
29041                     this.hide();
29042                 }
29043             }
29044         });
29045          
29046         this.progressDialog.render(Roo.get(document.body));
29047          
29048         this.progress = new Roo.bootstrap.Progress({
29049             cls : 'roo-document-manager-progress',
29050             active : true,
29051             striped : true
29052         });
29053         
29054         this.progress.render(this.progressDialog.getChildContainer());
29055         
29056         this.progressBar = new Roo.bootstrap.ProgressBar({
29057             cls : 'roo-document-manager-progress-bar',
29058             aria_valuenow : 0,
29059             aria_valuemin : 0,
29060             aria_valuemax : 12,
29061             panel : 'success'
29062         });
29063         
29064         this.progressBar.render(this.progress.getChildContainer());
29065     },
29066     
29067     onUploaderClick : function(e)
29068     {
29069         e.preventDefault();
29070      
29071         if(this.fireEvent('beforeselectfile', this) != false){
29072             this.selectorEl.dom.click();
29073         }
29074         
29075     },
29076     
29077     onFileSelected : function(e)
29078     {
29079         e.preventDefault();
29080         
29081         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29082             return;
29083         }
29084         
29085         Roo.each(this.selectorEl.dom.files, function(file){
29086             if(this.fireEvent('inspect', this, file) != false){
29087                 this.files.push(file);
29088             }
29089         }, this);
29090         
29091         this.queue();
29092         
29093     },
29094     
29095     queue : function()
29096     {
29097         this.selectorEl.dom.value = '';
29098         
29099         if(!this.files || !this.files.length){
29100             return;
29101         }
29102         
29103         if(this.boxes > 0 && this.files.length > this.boxes){
29104             this.files = this.files.slice(0, this.boxes);
29105         }
29106         
29107         this.uploader.show();
29108         
29109         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29110             this.uploader.hide();
29111         }
29112         
29113         var _this = this;
29114         
29115         var files = [];
29116         
29117         var docs = [];
29118         
29119         Roo.each(this.files, function(file){
29120             
29121             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29122                 var f = this.renderPreview(file);
29123                 files.push(f);
29124                 return;
29125             }
29126             
29127             if(file.type.indexOf('image') != -1){
29128                 this.delegates.push(
29129                     (function(){
29130                         _this.process(file);
29131                     }).createDelegate(this)
29132                 );
29133         
29134                 return;
29135             }
29136             
29137             docs.push(
29138                 (function(){
29139                     _this.process(file);
29140                 }).createDelegate(this)
29141             );
29142             
29143         }, this);
29144         
29145         this.files = files;
29146         
29147         this.delegates = this.delegates.concat(docs);
29148         
29149         if(!this.delegates.length){
29150             this.refresh();
29151             return;
29152         }
29153         
29154         this.progressBar.aria_valuemax = this.delegates.length;
29155         
29156         this.arrange();
29157         
29158         return;
29159     },
29160     
29161     arrange : function()
29162     {
29163         if(!this.delegates.length){
29164             this.progressDialog.hide();
29165             this.refresh();
29166             return;
29167         }
29168         
29169         var delegate = this.delegates.shift();
29170         
29171         this.progressDialog.show();
29172         
29173         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29174         
29175         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29176         
29177         delegate();
29178     },
29179     
29180     refresh : function()
29181     {
29182         this.uploader.show();
29183         
29184         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29185             this.uploader.hide();
29186         }
29187         
29188         Roo.isTouch ? this.closable(false) : this.closable(true);
29189         
29190         this.fireEvent('refresh', this);
29191     },
29192     
29193     onRemove : function(e, el, o)
29194     {
29195         e.preventDefault();
29196         
29197         this.fireEvent('remove', this, o);
29198         
29199     },
29200     
29201     remove : function(o)
29202     {
29203         var files = [];
29204         
29205         Roo.each(this.files, function(file){
29206             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29207                 files.push(file);
29208                 return;
29209             }
29210
29211             o.target.remove();
29212
29213         }, this);
29214         
29215         this.files = files;
29216         
29217         this.refresh();
29218     },
29219     
29220     clear : function()
29221     {
29222         Roo.each(this.files, function(file){
29223             if(!file.target){
29224                 return;
29225             }
29226             
29227             file.target.remove();
29228
29229         }, this);
29230         
29231         this.files = [];
29232         
29233         this.refresh();
29234     },
29235     
29236     onClick : function(e, el, o)
29237     {
29238         e.preventDefault();
29239         
29240         this.fireEvent('click', this, o);
29241         
29242     },
29243     
29244     closable : function(closable)
29245     {
29246         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29247             
29248             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29249             
29250             if(closable){
29251                 el.show();
29252                 return;
29253             }
29254             
29255             el.hide();
29256             
29257         }, this);
29258     },
29259     
29260     xhrOnLoad : function(xhr)
29261     {
29262         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29263             el.remove();
29264         }, this);
29265         
29266         if (xhr.readyState !== 4) {
29267             this.arrange();
29268             this.fireEvent('exception', this, xhr);
29269             return;
29270         }
29271
29272         var response = Roo.decode(xhr.responseText);
29273         
29274         if(!response.success){
29275             this.arrange();
29276             this.fireEvent('exception', this, xhr);
29277             return;
29278         }
29279         
29280         var file = this.renderPreview(response.data);
29281         
29282         this.files.push(file);
29283         
29284         this.arrange();
29285         
29286         this.fireEvent('afterupload', this, xhr);
29287         
29288     },
29289     
29290     xhrOnError : function(xhr)
29291     {
29292         Roo.log('xhr on error');
29293         
29294         var response = Roo.decode(xhr.responseText);
29295           
29296         Roo.log(response);
29297         
29298         this.arrange();
29299     },
29300     
29301     process : function(file)
29302     {
29303         if(this.fireEvent('process', this, file) !== false){
29304             if(this.editable && file.type.indexOf('image') != -1){
29305                 this.fireEvent('edit', this, file);
29306                 return;
29307             }
29308
29309             this.uploadStart(file, false);
29310
29311             return;
29312         }
29313         
29314     },
29315     
29316     uploadStart : function(file, crop)
29317     {
29318         this.xhr = new XMLHttpRequest();
29319         
29320         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29321             this.arrange();
29322             return;
29323         }
29324         
29325         file.xhr = this.xhr;
29326             
29327         this.managerEl.createChild({
29328             tag : 'div',
29329             cls : 'roo-document-manager-loading',
29330             cn : [
29331                 {
29332                     tag : 'div',
29333                     tooltip : file.name,
29334                     cls : 'roo-document-manager-thumb',
29335                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29336                 }
29337             ]
29338
29339         });
29340
29341         this.xhr.open(this.method, this.url, true);
29342         
29343         var headers = {
29344             "Accept": "application/json",
29345             "Cache-Control": "no-cache",
29346             "X-Requested-With": "XMLHttpRequest"
29347         };
29348         
29349         for (var headerName in headers) {
29350             var headerValue = headers[headerName];
29351             if (headerValue) {
29352                 this.xhr.setRequestHeader(headerName, headerValue);
29353             }
29354         }
29355         
29356         var _this = this;
29357         
29358         this.xhr.onload = function()
29359         {
29360             _this.xhrOnLoad(_this.xhr);
29361         }
29362         
29363         this.xhr.onerror = function()
29364         {
29365             _this.xhrOnError(_this.xhr);
29366         }
29367         
29368         var formData = new FormData();
29369
29370         formData.append('returnHTML', 'NO');
29371         
29372         if(crop){
29373             formData.append('crop', crop);
29374         }
29375         
29376         formData.append(this.paramName, file, file.name);
29377         
29378         var options = {
29379             file : file, 
29380             manually : false
29381         };
29382         
29383         if(this.fireEvent('prepare', this, formData, options) != false){
29384             
29385             if(options.manually){
29386                 return;
29387             }
29388             
29389             this.xhr.send(formData);
29390             return;
29391         };
29392         
29393         this.uploadCancel();
29394     },
29395     
29396     uploadCancel : function()
29397     {
29398         if (this.xhr) {
29399             this.xhr.abort();
29400         }
29401         
29402         this.delegates = [];
29403         
29404         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29405             el.remove();
29406         }, this);
29407         
29408         this.arrange();
29409     },
29410     
29411     renderPreview : function(file)
29412     {
29413         if(typeof(file.target) != 'undefined' && file.target){
29414             return file;
29415         }
29416         
29417         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29418         
29419         var previewEl = this.managerEl.createChild({
29420             tag : 'div',
29421             cls : 'roo-document-manager-preview',
29422             cn : [
29423                 {
29424                     tag : 'div',
29425                     tooltip : file[this.toolTipName],
29426                     cls : 'roo-document-manager-thumb',
29427                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29428                 },
29429                 {
29430                     tag : 'button',
29431                     cls : 'close',
29432                     html : '<i class="fa fa-times-circle"></i>'
29433                 }
29434             ]
29435         });
29436
29437         var close = previewEl.select('button.close', true).first();
29438
29439         close.on('click', this.onRemove, this, file);
29440
29441         file.target = previewEl;
29442
29443         var image = previewEl.select('img', true).first();
29444         
29445         var _this = this;
29446         
29447         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29448         
29449         image.on('click', this.onClick, this, file);
29450         
29451         this.fireEvent('previewrendered', this, file);
29452         
29453         return file;
29454         
29455     },
29456     
29457     onPreviewLoad : function(file, image)
29458     {
29459         if(typeof(file.target) == 'undefined' || !file.target){
29460             return;
29461         }
29462         
29463         var width = image.dom.naturalWidth || image.dom.width;
29464         var height = image.dom.naturalHeight || image.dom.height;
29465         
29466         if(!this.previewResize) {
29467             return;
29468         }
29469         
29470         if(width > height){
29471             file.target.addClass('wide');
29472             return;
29473         }
29474         
29475         file.target.addClass('tall');
29476         return;
29477         
29478     },
29479     
29480     uploadFromSource : function(file, crop)
29481     {
29482         this.xhr = new XMLHttpRequest();
29483         
29484         this.managerEl.createChild({
29485             tag : 'div',
29486             cls : 'roo-document-manager-loading',
29487             cn : [
29488                 {
29489                     tag : 'div',
29490                     tooltip : file.name,
29491                     cls : 'roo-document-manager-thumb',
29492                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29493                 }
29494             ]
29495
29496         });
29497
29498         this.xhr.open(this.method, this.url, true);
29499         
29500         var headers = {
29501             "Accept": "application/json",
29502             "Cache-Control": "no-cache",
29503             "X-Requested-With": "XMLHttpRequest"
29504         };
29505         
29506         for (var headerName in headers) {
29507             var headerValue = headers[headerName];
29508             if (headerValue) {
29509                 this.xhr.setRequestHeader(headerName, headerValue);
29510             }
29511         }
29512         
29513         var _this = this;
29514         
29515         this.xhr.onload = function()
29516         {
29517             _this.xhrOnLoad(_this.xhr);
29518         }
29519         
29520         this.xhr.onerror = function()
29521         {
29522             _this.xhrOnError(_this.xhr);
29523         }
29524         
29525         var formData = new FormData();
29526
29527         formData.append('returnHTML', 'NO');
29528         
29529         formData.append('crop', crop);
29530         
29531         if(typeof(file.filename) != 'undefined'){
29532             formData.append('filename', file.filename);
29533         }
29534         
29535         if(typeof(file.mimetype) != 'undefined'){
29536             formData.append('mimetype', file.mimetype);
29537         }
29538         
29539         Roo.log(formData);
29540         
29541         if(this.fireEvent('prepare', this, formData) != false){
29542             this.xhr.send(formData);
29543         };
29544     }
29545 });
29546
29547 /*
29548 * Licence: LGPL
29549 */
29550
29551 /**
29552  * @class Roo.bootstrap.DocumentViewer
29553  * @extends Roo.bootstrap.Component
29554  * Bootstrap DocumentViewer class
29555  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29556  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29557  * 
29558  * @constructor
29559  * Create a new DocumentViewer
29560  * @param {Object} config The config object
29561  */
29562
29563 Roo.bootstrap.DocumentViewer = function(config){
29564     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29565     
29566     this.addEvents({
29567         /**
29568          * @event initial
29569          * Fire after initEvent
29570          * @param {Roo.bootstrap.DocumentViewer} this
29571          */
29572         "initial" : true,
29573         /**
29574          * @event click
29575          * Fire after click
29576          * @param {Roo.bootstrap.DocumentViewer} this
29577          */
29578         "click" : true,
29579         /**
29580          * @event download
29581          * Fire after download button
29582          * @param {Roo.bootstrap.DocumentViewer} this
29583          */
29584         "download" : true,
29585         /**
29586          * @event trash
29587          * Fire after trash button
29588          * @param {Roo.bootstrap.DocumentViewer} this
29589          */
29590         "trash" : true
29591         
29592     });
29593 };
29594
29595 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29596     
29597     showDownload : true,
29598     
29599     showTrash : true,
29600     
29601     getAutoCreate : function()
29602     {
29603         var cfg = {
29604             tag : 'div',
29605             cls : 'roo-document-viewer',
29606             cn : [
29607                 {
29608                     tag : 'div',
29609                     cls : 'roo-document-viewer-body',
29610                     cn : [
29611                         {
29612                             tag : 'div',
29613                             cls : 'roo-document-viewer-thumb',
29614                             cn : [
29615                                 {
29616                                     tag : 'img',
29617                                     cls : 'roo-document-viewer-image'
29618                                 }
29619                             ]
29620                         }
29621                     ]
29622                 },
29623                 {
29624                     tag : 'div',
29625                     cls : 'roo-document-viewer-footer',
29626                     cn : {
29627                         tag : 'div',
29628                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29629                         cn : [
29630                             {
29631                                 tag : 'div',
29632                                 cls : 'btn-group roo-document-viewer-download',
29633                                 cn : [
29634                                     {
29635                                         tag : 'button',
29636                                         cls : 'btn btn-default',
29637                                         html : '<i class="fa fa-download"></i>'
29638                                     }
29639                                 ]
29640                             },
29641                             {
29642                                 tag : 'div',
29643                                 cls : 'btn-group roo-document-viewer-trash',
29644                                 cn : [
29645                                     {
29646                                         tag : 'button',
29647                                         cls : 'btn btn-default',
29648                                         html : '<i class="fa fa-trash"></i>'
29649                                     }
29650                                 ]
29651                             }
29652                         ]
29653                     }
29654                 }
29655             ]
29656         };
29657         
29658         return cfg;
29659     },
29660     
29661     initEvents : function()
29662     {
29663         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29664         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29665         
29666         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29667         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29668         
29669         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29670         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29671         
29672         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29673         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29674         
29675         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29676         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29677         
29678         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29679         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29680         
29681         this.bodyEl.on('click', this.onClick, this);
29682         this.downloadBtn.on('click', this.onDownload, this);
29683         this.trashBtn.on('click', this.onTrash, this);
29684         
29685         this.downloadBtn.hide();
29686         this.trashBtn.hide();
29687         
29688         if(this.showDownload){
29689             this.downloadBtn.show();
29690         }
29691         
29692         if(this.showTrash){
29693             this.trashBtn.show();
29694         }
29695         
29696         if(!this.showDownload && !this.showTrash) {
29697             this.footerEl.hide();
29698         }
29699         
29700     },
29701     
29702     initial : function()
29703     {
29704         this.fireEvent('initial', this);
29705         
29706     },
29707     
29708     onClick : function(e)
29709     {
29710         e.preventDefault();
29711         
29712         this.fireEvent('click', this);
29713     },
29714     
29715     onDownload : function(e)
29716     {
29717         e.preventDefault();
29718         
29719         this.fireEvent('download', this);
29720     },
29721     
29722     onTrash : function(e)
29723     {
29724         e.preventDefault();
29725         
29726         this.fireEvent('trash', this);
29727     }
29728     
29729 });
29730 /*
29731  * - LGPL
29732  *
29733  * nav progress bar
29734  * 
29735  */
29736
29737 /**
29738  * @class Roo.bootstrap.NavProgressBar
29739  * @extends Roo.bootstrap.Component
29740  * Bootstrap NavProgressBar class
29741  * 
29742  * @constructor
29743  * Create a new nav progress bar
29744  * @param {Object} config The config object
29745  */
29746
29747 Roo.bootstrap.NavProgressBar = function(config){
29748     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29749
29750     this.bullets = this.bullets || [];
29751    
29752 //    Roo.bootstrap.NavProgressBar.register(this);
29753      this.addEvents({
29754         /**
29755              * @event changed
29756              * Fires when the active item changes
29757              * @param {Roo.bootstrap.NavProgressBar} this
29758              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29759              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29760          */
29761         'changed': true
29762      });
29763     
29764 };
29765
29766 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29767     
29768     bullets : [],
29769     barItems : [],
29770     
29771     getAutoCreate : function()
29772     {
29773         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29774         
29775         cfg = {
29776             tag : 'div',
29777             cls : 'roo-navigation-bar-group',
29778             cn : [
29779                 {
29780                     tag : 'div',
29781                     cls : 'roo-navigation-top-bar'
29782                 },
29783                 {
29784                     tag : 'div',
29785                     cls : 'roo-navigation-bullets-bar',
29786                     cn : [
29787                         {
29788                             tag : 'ul',
29789                             cls : 'roo-navigation-bar'
29790                         }
29791                     ]
29792                 },
29793                 
29794                 {
29795                     tag : 'div',
29796                     cls : 'roo-navigation-bottom-bar'
29797                 }
29798             ]
29799             
29800         };
29801         
29802         return cfg;
29803         
29804     },
29805     
29806     initEvents: function() 
29807     {
29808         
29809     },
29810     
29811     onRender : function(ct, position) 
29812     {
29813         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29814         
29815         if(this.bullets.length){
29816             Roo.each(this.bullets, function(b){
29817                this.addItem(b);
29818             }, this);
29819         }
29820         
29821         this.format();
29822         
29823     },
29824     
29825     addItem : function(cfg)
29826     {
29827         var item = new Roo.bootstrap.NavProgressItem(cfg);
29828         
29829         item.parentId = this.id;
29830         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29831         
29832         if(cfg.html){
29833             var top = new Roo.bootstrap.Element({
29834                 tag : 'div',
29835                 cls : 'roo-navigation-bar-text'
29836             });
29837             
29838             var bottom = new Roo.bootstrap.Element({
29839                 tag : 'div',
29840                 cls : 'roo-navigation-bar-text'
29841             });
29842             
29843             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29844             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29845             
29846             var topText = new Roo.bootstrap.Element({
29847                 tag : 'span',
29848                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29849             });
29850             
29851             var bottomText = new Roo.bootstrap.Element({
29852                 tag : 'span',
29853                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29854             });
29855             
29856             topText.onRender(top.el, null);
29857             bottomText.onRender(bottom.el, null);
29858             
29859             item.topEl = top;
29860             item.bottomEl = bottom;
29861         }
29862         
29863         this.barItems.push(item);
29864         
29865         return item;
29866     },
29867     
29868     getActive : function()
29869     {
29870         var active = false;
29871         
29872         Roo.each(this.barItems, function(v){
29873             
29874             if (!v.isActive()) {
29875                 return;
29876             }
29877             
29878             active = v;
29879             return false;
29880             
29881         });
29882         
29883         return active;
29884     },
29885     
29886     setActiveItem : function(item)
29887     {
29888         var prev = false;
29889         
29890         Roo.each(this.barItems, function(v){
29891             if (v.rid == item.rid) {
29892                 return ;
29893             }
29894             
29895             if (v.isActive()) {
29896                 v.setActive(false);
29897                 prev = v;
29898             }
29899         });
29900
29901         item.setActive(true);
29902         
29903         this.fireEvent('changed', this, item, prev);
29904     },
29905     
29906     getBarItem: function(rid)
29907     {
29908         var ret = false;
29909         
29910         Roo.each(this.barItems, function(e) {
29911             if (e.rid != rid) {
29912                 return;
29913             }
29914             
29915             ret =  e;
29916             return false;
29917         });
29918         
29919         return ret;
29920     },
29921     
29922     indexOfItem : function(item)
29923     {
29924         var index = false;
29925         
29926         Roo.each(this.barItems, function(v, i){
29927             
29928             if (v.rid != item.rid) {
29929                 return;
29930             }
29931             
29932             index = i;
29933             return false
29934         });
29935         
29936         return index;
29937     },
29938     
29939     setActiveNext : function()
29940     {
29941         var i = this.indexOfItem(this.getActive());
29942         
29943         if (i > this.barItems.length) {
29944             return;
29945         }
29946         
29947         this.setActiveItem(this.barItems[i+1]);
29948     },
29949     
29950     setActivePrev : function()
29951     {
29952         var i = this.indexOfItem(this.getActive());
29953         
29954         if (i  < 1) {
29955             return;
29956         }
29957         
29958         this.setActiveItem(this.barItems[i-1]);
29959     },
29960     
29961     format : function()
29962     {
29963         if(!this.barItems.length){
29964             return;
29965         }
29966      
29967         var width = 100 / this.barItems.length;
29968         
29969         Roo.each(this.barItems, function(i){
29970             i.el.setStyle('width', width + '%');
29971             i.topEl.el.setStyle('width', width + '%');
29972             i.bottomEl.el.setStyle('width', width + '%');
29973         }, this);
29974         
29975     }
29976     
29977 });
29978 /*
29979  * - LGPL
29980  *
29981  * Nav Progress Item
29982  * 
29983  */
29984
29985 /**
29986  * @class Roo.bootstrap.NavProgressItem
29987  * @extends Roo.bootstrap.Component
29988  * Bootstrap NavProgressItem class
29989  * @cfg {String} rid the reference id
29990  * @cfg {Boolean} active (true|false) Is item active default false
29991  * @cfg {Boolean} disabled (true|false) Is item active default false
29992  * @cfg {String} html
29993  * @cfg {String} position (top|bottom) text position default bottom
29994  * @cfg {String} icon show icon instead of number
29995  * 
29996  * @constructor
29997  * Create a new NavProgressItem
29998  * @param {Object} config The config object
29999  */
30000 Roo.bootstrap.NavProgressItem = function(config){
30001     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30002     this.addEvents({
30003         // raw events
30004         /**
30005          * @event click
30006          * The raw click event for the entire grid.
30007          * @param {Roo.bootstrap.NavProgressItem} this
30008          * @param {Roo.EventObject} e
30009          */
30010         "click" : true
30011     });
30012    
30013 };
30014
30015 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30016     
30017     rid : '',
30018     active : false,
30019     disabled : false,
30020     html : '',
30021     position : 'bottom',
30022     icon : false,
30023     
30024     getAutoCreate : function()
30025     {
30026         var iconCls = 'roo-navigation-bar-item-icon';
30027         
30028         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30029         
30030         var cfg = {
30031             tag: 'li',
30032             cls: 'roo-navigation-bar-item',
30033             cn : [
30034                 {
30035                     tag : 'i',
30036                     cls : iconCls
30037                 }
30038             ]
30039         };
30040         
30041         if(this.active){
30042             cfg.cls += ' active';
30043         }
30044         if(this.disabled){
30045             cfg.cls += ' disabled';
30046         }
30047         
30048         return cfg;
30049     },
30050     
30051     disable : function()
30052     {
30053         this.setDisabled(true);
30054     },
30055     
30056     enable : function()
30057     {
30058         this.setDisabled(false);
30059     },
30060     
30061     initEvents: function() 
30062     {
30063         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30064         
30065         this.iconEl.on('click', this.onClick, this);
30066     },
30067     
30068     onClick : function(e)
30069     {
30070         e.preventDefault();
30071         
30072         if(this.disabled){
30073             return;
30074         }
30075         
30076         if(this.fireEvent('click', this, e) === false){
30077             return;
30078         };
30079         
30080         this.parent().setActiveItem(this);
30081     },
30082     
30083     isActive: function () 
30084     {
30085         return this.active;
30086     },
30087     
30088     setActive : function(state)
30089     {
30090         if(this.active == state){
30091             return;
30092         }
30093         
30094         this.active = state;
30095         
30096         if (state) {
30097             this.el.addClass('active');
30098             return;
30099         }
30100         
30101         this.el.removeClass('active');
30102         
30103         return;
30104     },
30105     
30106     setDisabled : function(state)
30107     {
30108         if(this.disabled == state){
30109             return;
30110         }
30111         
30112         this.disabled = state;
30113         
30114         if (state) {
30115             this.el.addClass('disabled');
30116             return;
30117         }
30118         
30119         this.el.removeClass('disabled');
30120     },
30121     
30122     tooltipEl : function()
30123     {
30124         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30125     }
30126 });
30127  
30128
30129  /*
30130  * - LGPL
30131  *
30132  * FieldLabel
30133  * 
30134  */
30135
30136 /**
30137  * @class Roo.bootstrap.FieldLabel
30138  * @extends Roo.bootstrap.Component
30139  * Bootstrap FieldLabel class
30140  * @cfg {String} html contents of the element
30141  * @cfg {String} tag tag of the element default label
30142  * @cfg {String} cls class of the element
30143  * @cfg {String} target label target 
30144  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30145  * @cfg {String} invalidClass default "text-warning"
30146  * @cfg {String} validClass default "text-success"
30147  * @cfg {String} iconTooltip default "This field is required"
30148  * @cfg {String} indicatorpos (left|right) default left
30149  * 
30150  * @constructor
30151  * Create a new FieldLabel
30152  * @param {Object} config The config object
30153  */
30154
30155 Roo.bootstrap.FieldLabel = function(config){
30156     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30157     
30158     this.addEvents({
30159             /**
30160              * @event invalid
30161              * Fires after the field has been marked as invalid.
30162              * @param {Roo.form.FieldLabel} this
30163              * @param {String} msg The validation message
30164              */
30165             invalid : true,
30166             /**
30167              * @event valid
30168              * Fires after the field has been validated with no errors.
30169              * @param {Roo.form.FieldLabel} this
30170              */
30171             valid : true
30172         });
30173 };
30174
30175 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30176     
30177     tag: 'label',
30178     cls: '',
30179     html: '',
30180     target: '',
30181     allowBlank : true,
30182     invalidClass : 'has-warning',
30183     validClass : 'has-success',
30184     iconTooltip : 'This field is required',
30185     indicatorpos : 'left',
30186     
30187     getAutoCreate : function(){
30188         
30189         var cls = "";
30190         if (!this.allowBlank) {
30191             cls  = "visible";
30192         }
30193         
30194         var cfg = {
30195             tag : this.tag,
30196             cls : 'roo-bootstrap-field-label ' + this.cls,
30197             for : this.target,
30198             cn : [
30199                 {
30200                     tag : 'i',
30201                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30202                     tooltip : this.iconTooltip
30203                 },
30204                 {
30205                     tag : 'span',
30206                     html : this.html
30207                 }
30208             ] 
30209         };
30210         
30211         if(this.indicatorpos == 'right'){
30212             var cfg = {
30213                 tag : this.tag,
30214                 cls : 'roo-bootstrap-field-label ' + this.cls,
30215                 for : this.target,
30216                 cn : [
30217                     {
30218                         tag : 'span',
30219                         html : this.html
30220                     },
30221                     {
30222                         tag : 'i',
30223                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30224                         tooltip : this.iconTooltip
30225                     }
30226                 ] 
30227             };
30228         }
30229         
30230         return cfg;
30231     },
30232     
30233     initEvents: function() 
30234     {
30235         Roo.bootstrap.Element.superclass.initEvents.call(this);
30236         
30237         this.indicator = this.indicatorEl();
30238         
30239         if(this.indicator){
30240             this.indicator.removeClass('visible');
30241             this.indicator.addClass('invisible');
30242         }
30243         
30244         Roo.bootstrap.FieldLabel.register(this);
30245     },
30246     
30247     indicatorEl : function()
30248     {
30249         var indicator = this.el.select('i.roo-required-indicator',true).first();
30250         
30251         if(!indicator){
30252             return false;
30253         }
30254         
30255         return indicator;
30256         
30257     },
30258     
30259     /**
30260      * Mark this field as valid
30261      */
30262     markValid : function()
30263     {
30264         if(this.indicator){
30265             this.indicator.removeClass('visible');
30266             this.indicator.addClass('invisible');
30267         }
30268         
30269         this.el.removeClass(this.invalidClass);
30270         
30271         this.el.addClass(this.validClass);
30272         
30273         this.fireEvent('valid', this);
30274     },
30275     
30276     /**
30277      * Mark this field as invalid
30278      * @param {String} msg The validation message
30279      */
30280     markInvalid : function(msg)
30281     {
30282         if(this.indicator){
30283             this.indicator.removeClass('invisible');
30284             this.indicator.addClass('visible');
30285         }
30286         
30287         this.el.removeClass(this.validClass);
30288         
30289         this.el.addClass(this.invalidClass);
30290         
30291         this.fireEvent('invalid', this, msg);
30292     }
30293     
30294    
30295 });
30296
30297 Roo.apply(Roo.bootstrap.FieldLabel, {
30298     
30299     groups: {},
30300     
30301      /**
30302     * register a FieldLabel Group
30303     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30304     */
30305     register : function(label)
30306     {
30307         if(this.groups.hasOwnProperty(label.target)){
30308             return;
30309         }
30310      
30311         this.groups[label.target] = label;
30312         
30313     },
30314     /**
30315     * fetch a FieldLabel Group based on the target
30316     * @param {string} target
30317     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30318     */
30319     get: function(target) {
30320         if (typeof(this.groups[target]) == 'undefined') {
30321             return false;
30322         }
30323         
30324         return this.groups[target] ;
30325     }
30326 });
30327
30328  
30329
30330  /*
30331  * - LGPL
30332  *
30333  * page DateSplitField.
30334  * 
30335  */
30336
30337
30338 /**
30339  * @class Roo.bootstrap.DateSplitField
30340  * @extends Roo.bootstrap.Component
30341  * Bootstrap DateSplitField class
30342  * @cfg {string} fieldLabel - the label associated
30343  * @cfg {Number} labelWidth set the width of label (0-12)
30344  * @cfg {String} labelAlign (top|left)
30345  * @cfg {Boolean} dayAllowBlank (true|false) default false
30346  * @cfg {Boolean} monthAllowBlank (true|false) default false
30347  * @cfg {Boolean} yearAllowBlank (true|false) default false
30348  * @cfg {string} dayPlaceholder 
30349  * @cfg {string} monthPlaceholder
30350  * @cfg {string} yearPlaceholder
30351  * @cfg {string} dayFormat default 'd'
30352  * @cfg {string} monthFormat default 'm'
30353  * @cfg {string} yearFormat default 'Y'
30354  * @cfg {Number} labellg set the width of label (1-12)
30355  * @cfg {Number} labelmd set the width of label (1-12)
30356  * @cfg {Number} labelsm set the width of label (1-12)
30357  * @cfg {Number} labelxs set the width of label (1-12)
30358
30359  *     
30360  * @constructor
30361  * Create a new DateSplitField
30362  * @param {Object} config The config object
30363  */
30364
30365 Roo.bootstrap.DateSplitField = function(config){
30366     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30367     
30368     this.addEvents({
30369         // raw events
30370          /**
30371          * @event years
30372          * getting the data of years
30373          * @param {Roo.bootstrap.DateSplitField} this
30374          * @param {Object} years
30375          */
30376         "years" : true,
30377         /**
30378          * @event days
30379          * getting the data of days
30380          * @param {Roo.bootstrap.DateSplitField} this
30381          * @param {Object} days
30382          */
30383         "days" : true,
30384         /**
30385          * @event invalid
30386          * Fires after the field has been marked as invalid.
30387          * @param {Roo.form.Field} this
30388          * @param {String} msg The validation message
30389          */
30390         invalid : true,
30391        /**
30392          * @event valid
30393          * Fires after the field has been validated with no errors.
30394          * @param {Roo.form.Field} this
30395          */
30396         valid : true
30397     });
30398 };
30399
30400 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30401     
30402     fieldLabel : '',
30403     labelAlign : 'top',
30404     labelWidth : 3,
30405     dayAllowBlank : false,
30406     monthAllowBlank : false,
30407     yearAllowBlank : false,
30408     dayPlaceholder : '',
30409     monthPlaceholder : '',
30410     yearPlaceholder : '',
30411     dayFormat : 'd',
30412     monthFormat : 'm',
30413     yearFormat : 'Y',
30414     isFormField : true,
30415     labellg : 0,
30416     labelmd : 0,
30417     labelsm : 0,
30418     labelxs : 0,
30419     
30420     getAutoCreate : function()
30421     {
30422         var cfg = {
30423             tag : 'div',
30424             cls : 'row roo-date-split-field-group',
30425             cn : [
30426                 {
30427                     tag : 'input',
30428                     type : 'hidden',
30429                     cls : 'form-hidden-field roo-date-split-field-group-value',
30430                     name : this.name
30431                 }
30432             ]
30433         };
30434         
30435         var labelCls = 'col-md-12';
30436         var contentCls = 'col-md-4';
30437         
30438         if(this.fieldLabel){
30439             
30440             var label = {
30441                 tag : 'div',
30442                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30443                 cn : [
30444                     {
30445                         tag : 'label',
30446                         html : this.fieldLabel
30447                     }
30448                 ]
30449             };
30450             
30451             if(this.labelAlign == 'left'){
30452             
30453                 if(this.labelWidth > 12){
30454                     label.style = "width: " + this.labelWidth + 'px';
30455                 }
30456
30457                 if(this.labelWidth < 13 && this.labelmd == 0){
30458                     this.labelmd = this.labelWidth;
30459                 }
30460
30461                 if(this.labellg > 0){
30462                     labelCls = ' col-lg-' + this.labellg;
30463                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30464                 }
30465
30466                 if(this.labelmd > 0){
30467                     labelCls = ' col-md-' + this.labelmd;
30468                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30469                 }
30470
30471                 if(this.labelsm > 0){
30472                     labelCls = ' col-sm-' + this.labelsm;
30473                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30474                 }
30475
30476                 if(this.labelxs > 0){
30477                     labelCls = ' col-xs-' + this.labelxs;
30478                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30479                 }
30480             }
30481             
30482             label.cls += ' ' + labelCls;
30483             
30484             cfg.cn.push(label);
30485         }
30486         
30487         Roo.each(['day', 'month', 'year'], function(t){
30488             cfg.cn.push({
30489                 tag : 'div',
30490                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30491             });
30492         }, this);
30493         
30494         return cfg;
30495     },
30496     
30497     inputEl: function ()
30498     {
30499         return this.el.select('.roo-date-split-field-group-value', true).first();
30500     },
30501     
30502     onRender : function(ct, position) 
30503     {
30504         var _this = this;
30505         
30506         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30507         
30508         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30509         
30510         this.dayField = new Roo.bootstrap.ComboBox({
30511             allowBlank : this.dayAllowBlank,
30512             alwaysQuery : true,
30513             displayField : 'value',
30514             editable : false,
30515             fieldLabel : '',
30516             forceSelection : true,
30517             mode : 'local',
30518             placeholder : this.dayPlaceholder,
30519             selectOnFocus : true,
30520             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30521             triggerAction : 'all',
30522             typeAhead : true,
30523             valueField : 'value',
30524             store : new Roo.data.SimpleStore({
30525                 data : (function() {    
30526                     var days = [];
30527                     _this.fireEvent('days', _this, days);
30528                     return days;
30529                 })(),
30530                 fields : [ 'value' ]
30531             }),
30532             listeners : {
30533                 select : function (_self, record, index)
30534                 {
30535                     _this.setValue(_this.getValue());
30536                 }
30537             }
30538         });
30539
30540         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30541         
30542         this.monthField = new Roo.bootstrap.MonthField({
30543             after : '<i class=\"fa fa-calendar\"></i>',
30544             allowBlank : this.monthAllowBlank,
30545             placeholder : this.monthPlaceholder,
30546             readOnly : true,
30547             listeners : {
30548                 render : function (_self)
30549                 {
30550                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30551                         e.preventDefault();
30552                         _self.focus();
30553                     });
30554                 },
30555                 select : function (_self, oldvalue, newvalue)
30556                 {
30557                     _this.setValue(_this.getValue());
30558                 }
30559             }
30560         });
30561         
30562         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30563         
30564         this.yearField = new Roo.bootstrap.ComboBox({
30565             allowBlank : this.yearAllowBlank,
30566             alwaysQuery : true,
30567             displayField : 'value',
30568             editable : false,
30569             fieldLabel : '',
30570             forceSelection : true,
30571             mode : 'local',
30572             placeholder : this.yearPlaceholder,
30573             selectOnFocus : true,
30574             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30575             triggerAction : 'all',
30576             typeAhead : true,
30577             valueField : 'value',
30578             store : new Roo.data.SimpleStore({
30579                 data : (function() {
30580                     var years = [];
30581                     _this.fireEvent('years', _this, years);
30582                     return years;
30583                 })(),
30584                 fields : [ 'value' ]
30585             }),
30586             listeners : {
30587                 select : function (_self, record, index)
30588                 {
30589                     _this.setValue(_this.getValue());
30590                 }
30591             }
30592         });
30593
30594         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30595     },
30596     
30597     setValue : function(v, format)
30598     {
30599         this.inputEl.dom.value = v;
30600         
30601         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30602         
30603         var d = Date.parseDate(v, f);
30604         
30605         if(!d){
30606             this.validate();
30607             return;
30608         }
30609         
30610         this.setDay(d.format(this.dayFormat));
30611         this.setMonth(d.format(this.monthFormat));
30612         this.setYear(d.format(this.yearFormat));
30613         
30614         this.validate();
30615         
30616         return;
30617     },
30618     
30619     setDay : function(v)
30620     {
30621         this.dayField.setValue(v);
30622         this.inputEl.dom.value = this.getValue();
30623         this.validate();
30624         return;
30625     },
30626     
30627     setMonth : function(v)
30628     {
30629         this.monthField.setValue(v, true);
30630         this.inputEl.dom.value = this.getValue();
30631         this.validate();
30632         return;
30633     },
30634     
30635     setYear : function(v)
30636     {
30637         this.yearField.setValue(v);
30638         this.inputEl.dom.value = this.getValue();
30639         this.validate();
30640         return;
30641     },
30642     
30643     getDay : function()
30644     {
30645         return this.dayField.getValue();
30646     },
30647     
30648     getMonth : function()
30649     {
30650         return this.monthField.getValue();
30651     },
30652     
30653     getYear : function()
30654     {
30655         return this.yearField.getValue();
30656     },
30657     
30658     getValue : function()
30659     {
30660         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30661         
30662         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30663         
30664         return date;
30665     },
30666     
30667     reset : function()
30668     {
30669         this.setDay('');
30670         this.setMonth('');
30671         this.setYear('');
30672         this.inputEl.dom.value = '';
30673         this.validate();
30674         return;
30675     },
30676     
30677     validate : function()
30678     {
30679         var d = this.dayField.validate();
30680         var m = this.monthField.validate();
30681         var y = this.yearField.validate();
30682         
30683         var valid = true;
30684         
30685         if(
30686                 (!this.dayAllowBlank && !d) ||
30687                 (!this.monthAllowBlank && !m) ||
30688                 (!this.yearAllowBlank && !y)
30689         ){
30690             valid = false;
30691         }
30692         
30693         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30694             return valid;
30695         }
30696         
30697         if(valid){
30698             this.markValid();
30699             return valid;
30700         }
30701         
30702         this.markInvalid();
30703         
30704         return valid;
30705     },
30706     
30707     markValid : function()
30708     {
30709         
30710         var label = this.el.select('label', true).first();
30711         var icon = this.el.select('i.fa-star', true).first();
30712
30713         if(label && icon){
30714             icon.remove();
30715         }
30716         
30717         this.fireEvent('valid', this);
30718     },
30719     
30720      /**
30721      * Mark this field as invalid
30722      * @param {String} msg The validation message
30723      */
30724     markInvalid : function(msg)
30725     {
30726         
30727         var label = this.el.select('label', true).first();
30728         var icon = this.el.select('i.fa-star', true).first();
30729
30730         if(label && !icon){
30731             this.el.select('.roo-date-split-field-label', true).createChild({
30732                 tag : 'i',
30733                 cls : 'text-danger fa fa-lg fa-star',
30734                 tooltip : 'This field is required',
30735                 style : 'margin-right:5px;'
30736             }, label, true);
30737         }
30738         
30739         this.fireEvent('invalid', this, msg);
30740     },
30741     
30742     clearInvalid : function()
30743     {
30744         var label = this.el.select('label', true).first();
30745         var icon = this.el.select('i.fa-star', true).first();
30746
30747         if(label && icon){
30748             icon.remove();
30749         }
30750         
30751         this.fireEvent('valid', this);
30752     },
30753     
30754     getName: function()
30755     {
30756         return this.name;
30757     }
30758     
30759 });
30760
30761  /**
30762  *
30763  * This is based on 
30764  * http://masonry.desandro.com
30765  *
30766  * The idea is to render all the bricks based on vertical width...
30767  *
30768  * The original code extends 'outlayer' - we might need to use that....
30769  * 
30770  */
30771
30772
30773 /**
30774  * @class Roo.bootstrap.LayoutMasonry
30775  * @extends Roo.bootstrap.Component
30776  * Bootstrap Layout Masonry class
30777  * 
30778  * @constructor
30779  * Create a new Element
30780  * @param {Object} config The config object
30781  */
30782
30783 Roo.bootstrap.LayoutMasonry = function(config){
30784     
30785     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30786     
30787     this.bricks = [];
30788     
30789     Roo.bootstrap.LayoutMasonry.register(this);
30790     
30791     this.addEvents({
30792         // raw events
30793         /**
30794          * @event layout
30795          * Fire after layout the items
30796          * @param {Roo.bootstrap.LayoutMasonry} this
30797          * @param {Roo.EventObject} e
30798          */
30799         "layout" : true
30800     });
30801     
30802 };
30803
30804 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30805     
30806     /**
30807      * @cfg {Boolean} isLayoutInstant = no animation?
30808      */   
30809     isLayoutInstant : false, // needed?
30810    
30811     /**
30812      * @cfg {Number} boxWidth  width of the columns
30813      */   
30814     boxWidth : 450,
30815     
30816       /**
30817      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30818      */   
30819     boxHeight : 0,
30820     
30821     /**
30822      * @cfg {Number} padWidth padding below box..
30823      */   
30824     padWidth : 10, 
30825     
30826     /**
30827      * @cfg {Number} gutter gutter width..
30828      */   
30829     gutter : 10,
30830     
30831      /**
30832      * @cfg {Number} maxCols maximum number of columns
30833      */   
30834     
30835     maxCols: 0,
30836     
30837     /**
30838      * @cfg {Boolean} isAutoInitial defalut true
30839      */   
30840     isAutoInitial : true, 
30841     
30842     containerWidth: 0,
30843     
30844     /**
30845      * @cfg {Boolean} isHorizontal defalut false
30846      */   
30847     isHorizontal : false, 
30848
30849     currentSize : null,
30850     
30851     tag: 'div',
30852     
30853     cls: '',
30854     
30855     bricks: null, //CompositeElement
30856     
30857     cols : 1,
30858     
30859     _isLayoutInited : false,
30860     
30861 //    isAlternative : false, // only use for vertical layout...
30862     
30863     /**
30864      * @cfg {Number} alternativePadWidth padding below box..
30865      */   
30866     alternativePadWidth : 50,
30867     
30868     selectedBrick : [],
30869     
30870     getAutoCreate : function(){
30871         
30872         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30873         
30874         var cfg = {
30875             tag: this.tag,
30876             cls: 'blog-masonary-wrapper ' + this.cls,
30877             cn : {
30878                 cls : 'mas-boxes masonary'
30879             }
30880         };
30881         
30882         return cfg;
30883     },
30884     
30885     getChildContainer: function( )
30886     {
30887         if (this.boxesEl) {
30888             return this.boxesEl;
30889         }
30890         
30891         this.boxesEl = this.el.select('.mas-boxes').first();
30892         
30893         return this.boxesEl;
30894     },
30895     
30896     
30897     initEvents : function()
30898     {
30899         var _this = this;
30900         
30901         if(this.isAutoInitial){
30902             Roo.log('hook children rendered');
30903             this.on('childrenrendered', function() {
30904                 Roo.log('children rendered');
30905                 _this.initial();
30906             } ,this);
30907         }
30908     },
30909     
30910     initial : function()
30911     {
30912         this.selectedBrick = [];
30913         
30914         this.currentSize = this.el.getBox(true);
30915         
30916         Roo.EventManager.onWindowResize(this.resize, this); 
30917
30918         if(!this.isAutoInitial){
30919             this.layout();
30920             return;
30921         }
30922         
30923         this.layout();
30924         
30925         return;
30926         //this.layout.defer(500,this);
30927         
30928     },
30929     
30930     resize : function()
30931     {
30932         var cs = this.el.getBox(true);
30933         
30934         if (
30935                 this.currentSize.width == cs.width && 
30936                 this.currentSize.x == cs.x && 
30937                 this.currentSize.height == cs.height && 
30938                 this.currentSize.y == cs.y 
30939         ) {
30940             Roo.log("no change in with or X or Y");
30941             return;
30942         }
30943         
30944         this.currentSize = cs;
30945         
30946         this.layout();
30947         
30948     },
30949     
30950     layout : function()
30951     {   
30952         this._resetLayout();
30953         
30954         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30955         
30956         this.layoutItems( isInstant );
30957       
30958         this._isLayoutInited = true;
30959         
30960         this.fireEvent('layout', this);
30961         
30962     },
30963     
30964     _resetLayout : function()
30965     {
30966         if(this.isHorizontal){
30967             this.horizontalMeasureColumns();
30968             return;
30969         }
30970         
30971         this.verticalMeasureColumns();
30972         
30973     },
30974     
30975     verticalMeasureColumns : function()
30976     {
30977         this.getContainerWidth();
30978         
30979 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30980 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30981 //            return;
30982 //        }
30983         
30984         var boxWidth = this.boxWidth + this.padWidth;
30985         
30986         if(this.containerWidth < this.boxWidth){
30987             boxWidth = this.containerWidth
30988         }
30989         
30990         var containerWidth = this.containerWidth;
30991         
30992         var cols = Math.floor(containerWidth / boxWidth);
30993         
30994         this.cols = Math.max( cols, 1 );
30995         
30996         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30997         
30998         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30999         
31000         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31001         
31002         this.colWidth = boxWidth + avail - this.padWidth;
31003         
31004         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31005         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31006     },
31007     
31008     horizontalMeasureColumns : function()
31009     {
31010         this.getContainerWidth();
31011         
31012         var boxWidth = this.boxWidth;
31013         
31014         if(this.containerWidth < boxWidth){
31015             boxWidth = this.containerWidth;
31016         }
31017         
31018         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31019         
31020         this.el.setHeight(boxWidth);
31021         
31022     },
31023     
31024     getContainerWidth : function()
31025     {
31026         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31027     },
31028     
31029     layoutItems : function( isInstant )
31030     {
31031         Roo.log(this.bricks);
31032         
31033         var items = Roo.apply([], this.bricks);
31034         
31035         if(this.isHorizontal){
31036             this._horizontalLayoutItems( items , isInstant );
31037             return;
31038         }
31039         
31040 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31041 //            this._verticalAlternativeLayoutItems( items , isInstant );
31042 //            return;
31043 //        }
31044         
31045         this._verticalLayoutItems( items , isInstant );
31046         
31047     },
31048     
31049     _verticalLayoutItems : function ( items , isInstant)
31050     {
31051         if ( !items || !items.length ) {
31052             return;
31053         }
31054         
31055         var standard = [
31056             ['xs', 'xs', 'xs', 'tall'],
31057             ['xs', 'xs', 'tall'],
31058             ['xs', 'xs', 'sm'],
31059             ['xs', 'xs', 'xs'],
31060             ['xs', 'tall'],
31061             ['xs', 'sm'],
31062             ['xs', 'xs'],
31063             ['xs'],
31064             
31065             ['sm', 'xs', 'xs'],
31066             ['sm', 'xs'],
31067             ['sm'],
31068             
31069             ['tall', 'xs', 'xs', 'xs'],
31070             ['tall', 'xs', 'xs'],
31071             ['tall', 'xs'],
31072             ['tall']
31073             
31074         ];
31075         
31076         var queue = [];
31077         
31078         var boxes = [];
31079         
31080         var box = [];
31081         
31082         Roo.each(items, function(item, k){
31083             
31084             switch (item.size) {
31085                 // these layouts take up a full box,
31086                 case 'md' :
31087                 case 'md-left' :
31088                 case 'md-right' :
31089                 case 'wide' :
31090                     
31091                     if(box.length){
31092                         boxes.push(box);
31093                         box = [];
31094                     }
31095                     
31096                     boxes.push([item]);
31097                     
31098                     break;
31099                     
31100                 case 'xs' :
31101                 case 'sm' :
31102                 case 'tall' :
31103                     
31104                     box.push(item);
31105                     
31106                     break;
31107                 default :
31108                     break;
31109                     
31110             }
31111             
31112         }, this);
31113         
31114         if(box.length){
31115             boxes.push(box);
31116             box = [];
31117         }
31118         
31119         var filterPattern = function(box, length)
31120         {
31121             if(!box.length){
31122                 return;
31123             }
31124             
31125             var match = false;
31126             
31127             var pattern = box.slice(0, length);
31128             
31129             var format = [];
31130             
31131             Roo.each(pattern, function(i){
31132                 format.push(i.size);
31133             }, this);
31134             
31135             Roo.each(standard, function(s){
31136                 
31137                 if(String(s) != String(format)){
31138                     return;
31139                 }
31140                 
31141                 match = true;
31142                 return false;
31143                 
31144             }, this);
31145             
31146             if(!match && length == 1){
31147                 return;
31148             }
31149             
31150             if(!match){
31151                 filterPattern(box, length - 1);
31152                 return;
31153             }
31154                 
31155             queue.push(pattern);
31156
31157             box = box.slice(length, box.length);
31158
31159             filterPattern(box, 4);
31160
31161             return;
31162             
31163         }
31164         
31165         Roo.each(boxes, function(box, k){
31166             
31167             if(!box.length){
31168                 return;
31169             }
31170             
31171             if(box.length == 1){
31172                 queue.push(box);
31173                 return;
31174             }
31175             
31176             filterPattern(box, 4);
31177             
31178         }, this);
31179         
31180         this._processVerticalLayoutQueue( queue, isInstant );
31181         
31182     },
31183     
31184 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31185 //    {
31186 //        if ( !items || !items.length ) {
31187 //            return;
31188 //        }
31189 //
31190 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31191 //        
31192 //    },
31193     
31194     _horizontalLayoutItems : function ( items , isInstant)
31195     {
31196         if ( !items || !items.length || items.length < 3) {
31197             return;
31198         }
31199         
31200         items.reverse();
31201         
31202         var eItems = items.slice(0, 3);
31203         
31204         items = items.slice(3, items.length);
31205         
31206         var standard = [
31207             ['xs', 'xs', 'xs', 'wide'],
31208             ['xs', 'xs', 'wide'],
31209             ['xs', 'xs', 'sm'],
31210             ['xs', 'xs', 'xs'],
31211             ['xs', 'wide'],
31212             ['xs', 'sm'],
31213             ['xs', 'xs'],
31214             ['xs'],
31215             
31216             ['sm', 'xs', 'xs'],
31217             ['sm', 'xs'],
31218             ['sm'],
31219             
31220             ['wide', 'xs', 'xs', 'xs'],
31221             ['wide', 'xs', 'xs'],
31222             ['wide', 'xs'],
31223             ['wide'],
31224             
31225             ['wide-thin']
31226         ];
31227         
31228         var queue = [];
31229         
31230         var boxes = [];
31231         
31232         var box = [];
31233         
31234         Roo.each(items, function(item, k){
31235             
31236             switch (item.size) {
31237                 case 'md' :
31238                 case 'md-left' :
31239                 case 'md-right' :
31240                 case 'tall' :
31241                     
31242                     if(box.length){
31243                         boxes.push(box);
31244                         box = [];
31245                     }
31246                     
31247                     boxes.push([item]);
31248                     
31249                     break;
31250                     
31251                 case 'xs' :
31252                 case 'sm' :
31253                 case 'wide' :
31254                 case 'wide-thin' :
31255                     
31256                     box.push(item);
31257                     
31258                     break;
31259                 default :
31260                     break;
31261                     
31262             }
31263             
31264         }, this);
31265         
31266         if(box.length){
31267             boxes.push(box);
31268             box = [];
31269         }
31270         
31271         var filterPattern = function(box, length)
31272         {
31273             if(!box.length){
31274                 return;
31275             }
31276             
31277             var match = false;
31278             
31279             var pattern = box.slice(0, length);
31280             
31281             var format = [];
31282             
31283             Roo.each(pattern, function(i){
31284                 format.push(i.size);
31285             }, this);
31286             
31287             Roo.each(standard, function(s){
31288                 
31289                 if(String(s) != String(format)){
31290                     return;
31291                 }
31292                 
31293                 match = true;
31294                 return false;
31295                 
31296             }, this);
31297             
31298             if(!match && length == 1){
31299                 return;
31300             }
31301             
31302             if(!match){
31303                 filterPattern(box, length - 1);
31304                 return;
31305             }
31306                 
31307             queue.push(pattern);
31308
31309             box = box.slice(length, box.length);
31310
31311             filterPattern(box, 4);
31312
31313             return;
31314             
31315         }
31316         
31317         Roo.each(boxes, function(box, k){
31318             
31319             if(!box.length){
31320                 return;
31321             }
31322             
31323             if(box.length == 1){
31324                 queue.push(box);
31325                 return;
31326             }
31327             
31328             filterPattern(box, 4);
31329             
31330         }, this);
31331         
31332         
31333         var prune = [];
31334         
31335         var pos = this.el.getBox(true);
31336         
31337         var minX = pos.x;
31338         
31339         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31340         
31341         var hit_end = false;
31342         
31343         Roo.each(queue, function(box){
31344             
31345             if(hit_end){
31346                 
31347                 Roo.each(box, function(b){
31348                 
31349                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31350                     b.el.hide();
31351
31352                 }, this);
31353
31354                 return;
31355             }
31356             
31357             var mx = 0;
31358             
31359             Roo.each(box, function(b){
31360                 
31361                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31362                 b.el.show();
31363
31364                 mx = Math.max(mx, b.x);
31365                 
31366             }, this);
31367             
31368             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31369             
31370             if(maxX < minX){
31371                 
31372                 Roo.each(box, function(b){
31373                 
31374                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31375                     b.el.hide();
31376                     
31377                 }, this);
31378                 
31379                 hit_end = true;
31380                 
31381                 return;
31382             }
31383             
31384             prune.push(box);
31385             
31386         }, this);
31387         
31388         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31389     },
31390     
31391     /** Sets position of item in DOM
31392     * @param {Element} item
31393     * @param {Number} x - horizontal position
31394     * @param {Number} y - vertical position
31395     * @param {Boolean} isInstant - disables transitions
31396     */
31397     _processVerticalLayoutQueue : function( queue, isInstant )
31398     {
31399         var pos = this.el.getBox(true);
31400         var x = pos.x;
31401         var y = pos.y;
31402         var maxY = [];
31403         
31404         for (var i = 0; i < this.cols; i++){
31405             maxY[i] = pos.y;
31406         }
31407         
31408         Roo.each(queue, function(box, k){
31409             
31410             var col = k % this.cols;
31411             
31412             Roo.each(box, function(b,kk){
31413                 
31414                 b.el.position('absolute');
31415                 
31416                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31417                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31418                 
31419                 if(b.size == 'md-left' || b.size == 'md-right'){
31420                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31421                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31422                 }
31423                 
31424                 b.el.setWidth(width);
31425                 b.el.setHeight(height);
31426                 // iframe?
31427                 b.el.select('iframe',true).setSize(width,height);
31428                 
31429             }, this);
31430             
31431             for (var i = 0; i < this.cols; i++){
31432                 
31433                 if(maxY[i] < maxY[col]){
31434                     col = i;
31435                     continue;
31436                 }
31437                 
31438                 col = Math.min(col, i);
31439                 
31440             }
31441             
31442             x = pos.x + col * (this.colWidth + this.padWidth);
31443             
31444             y = maxY[col];
31445             
31446             var positions = [];
31447             
31448             switch (box.length){
31449                 case 1 :
31450                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31451                     break;
31452                 case 2 :
31453                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31454                     break;
31455                 case 3 :
31456                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31457                     break;
31458                 case 4 :
31459                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31460                     break;
31461                 default :
31462                     break;
31463             }
31464             
31465             Roo.each(box, function(b,kk){
31466                 
31467                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31468                 
31469                 var sz = b.el.getSize();
31470                 
31471                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31472                 
31473             }, this);
31474             
31475         }, this);
31476         
31477         var mY = 0;
31478         
31479         for (var i = 0; i < this.cols; i++){
31480             mY = Math.max(mY, maxY[i]);
31481         }
31482         
31483         this.el.setHeight(mY - pos.y);
31484         
31485     },
31486     
31487 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31488 //    {
31489 //        var pos = this.el.getBox(true);
31490 //        var x = pos.x;
31491 //        var y = pos.y;
31492 //        var maxX = pos.right;
31493 //        
31494 //        var maxHeight = 0;
31495 //        
31496 //        Roo.each(items, function(item, k){
31497 //            
31498 //            var c = k % 2;
31499 //            
31500 //            item.el.position('absolute');
31501 //                
31502 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31503 //
31504 //            item.el.setWidth(width);
31505 //
31506 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31507 //
31508 //            item.el.setHeight(height);
31509 //            
31510 //            if(c == 0){
31511 //                item.el.setXY([x, y], isInstant ? false : true);
31512 //            } else {
31513 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31514 //            }
31515 //            
31516 //            y = y + height + this.alternativePadWidth;
31517 //            
31518 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31519 //            
31520 //        }, this);
31521 //        
31522 //        this.el.setHeight(maxHeight);
31523 //        
31524 //    },
31525     
31526     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31527     {
31528         var pos = this.el.getBox(true);
31529         
31530         var minX = pos.x;
31531         var minY = pos.y;
31532         
31533         var maxX = pos.right;
31534         
31535         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31536         
31537         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31538         
31539         Roo.each(queue, function(box, k){
31540             
31541             Roo.each(box, function(b, kk){
31542                 
31543                 b.el.position('absolute');
31544                 
31545                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31546                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31547                 
31548                 if(b.size == 'md-left' || b.size == 'md-right'){
31549                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31550                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31551                 }
31552                 
31553                 b.el.setWidth(width);
31554                 b.el.setHeight(height);
31555                 
31556             }, this);
31557             
31558             if(!box.length){
31559                 return;
31560             }
31561             
31562             var positions = [];
31563             
31564             switch (box.length){
31565                 case 1 :
31566                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31567                     break;
31568                 case 2 :
31569                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31570                     break;
31571                 case 3 :
31572                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31573                     break;
31574                 case 4 :
31575                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31576                     break;
31577                 default :
31578                     break;
31579             }
31580             
31581             Roo.each(box, function(b,kk){
31582                 
31583                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31584                 
31585                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31586                 
31587             }, this);
31588             
31589         }, this);
31590         
31591     },
31592     
31593     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31594     {
31595         Roo.each(eItems, function(b,k){
31596             
31597             b.size = (k == 0) ? 'sm' : 'xs';
31598             b.x = (k == 0) ? 2 : 1;
31599             b.y = (k == 0) ? 2 : 1;
31600             
31601             b.el.position('absolute');
31602             
31603             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31604                 
31605             b.el.setWidth(width);
31606             
31607             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31608             
31609             b.el.setHeight(height);
31610             
31611         }, this);
31612
31613         var positions = [];
31614         
31615         positions.push({
31616             x : maxX - this.unitWidth * 2 - this.gutter,
31617             y : minY
31618         });
31619         
31620         positions.push({
31621             x : maxX - this.unitWidth,
31622             y : minY + (this.unitWidth + this.gutter) * 2
31623         });
31624         
31625         positions.push({
31626             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31627             y : minY
31628         });
31629         
31630         Roo.each(eItems, function(b,k){
31631             
31632             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31633
31634         }, this);
31635         
31636     },
31637     
31638     getVerticalOneBoxColPositions : function(x, y, box)
31639     {
31640         var pos = [];
31641         
31642         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31643         
31644         if(box[0].size == 'md-left'){
31645             rand = 0;
31646         }
31647         
31648         if(box[0].size == 'md-right'){
31649             rand = 1;
31650         }
31651         
31652         pos.push({
31653             x : x + (this.unitWidth + this.gutter) * rand,
31654             y : y
31655         });
31656         
31657         return pos;
31658     },
31659     
31660     getVerticalTwoBoxColPositions : function(x, y, box)
31661     {
31662         var pos = [];
31663         
31664         if(box[0].size == 'xs'){
31665             
31666             pos.push({
31667                 x : x,
31668                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31669             });
31670
31671             pos.push({
31672                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31673                 y : y
31674             });
31675             
31676             return pos;
31677             
31678         }
31679         
31680         pos.push({
31681             x : x,
31682             y : y
31683         });
31684
31685         pos.push({
31686             x : x + (this.unitWidth + this.gutter) * 2,
31687             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31688         });
31689         
31690         return pos;
31691         
31692     },
31693     
31694     getVerticalThreeBoxColPositions : function(x, y, box)
31695     {
31696         var pos = [];
31697         
31698         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31699             
31700             pos.push({
31701                 x : x,
31702                 y : y
31703             });
31704
31705             pos.push({
31706                 x : x + (this.unitWidth + this.gutter) * 1,
31707                 y : y
31708             });
31709             
31710             pos.push({
31711                 x : x + (this.unitWidth + this.gutter) * 2,
31712                 y : y
31713             });
31714             
31715             return pos;
31716             
31717         }
31718         
31719         if(box[0].size == 'xs' && box[1].size == 'xs'){
31720             
31721             pos.push({
31722                 x : x,
31723                 y : y
31724             });
31725
31726             pos.push({
31727                 x : x,
31728                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31729             });
31730             
31731             pos.push({
31732                 x : x + (this.unitWidth + this.gutter) * 1,
31733                 y : y
31734             });
31735             
31736             return pos;
31737             
31738         }
31739         
31740         pos.push({
31741             x : x,
31742             y : y
31743         });
31744
31745         pos.push({
31746             x : x + (this.unitWidth + this.gutter) * 2,
31747             y : y
31748         });
31749
31750         pos.push({
31751             x : x + (this.unitWidth + this.gutter) * 2,
31752             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31753         });
31754             
31755         return pos;
31756         
31757     },
31758     
31759     getVerticalFourBoxColPositions : function(x, y, box)
31760     {
31761         var pos = [];
31762         
31763         if(box[0].size == 'xs'){
31764             
31765             pos.push({
31766                 x : x,
31767                 y : y
31768             });
31769
31770             pos.push({
31771                 x : x,
31772                 y : y + (this.unitHeight + this.gutter) * 1
31773             });
31774             
31775             pos.push({
31776                 x : x,
31777                 y : y + (this.unitHeight + this.gutter) * 2
31778             });
31779             
31780             pos.push({
31781                 x : x + (this.unitWidth + this.gutter) * 1,
31782                 y : y
31783             });
31784             
31785             return pos;
31786             
31787         }
31788         
31789         pos.push({
31790             x : x,
31791             y : y
31792         });
31793
31794         pos.push({
31795             x : x + (this.unitWidth + this.gutter) * 2,
31796             y : y
31797         });
31798
31799         pos.push({
31800             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31801             y : y + (this.unitHeight + this.gutter) * 1
31802         });
31803
31804         pos.push({
31805             x : x + (this.unitWidth + this.gutter) * 2,
31806             y : y + (this.unitWidth + this.gutter) * 2
31807         });
31808
31809         return pos;
31810         
31811     },
31812     
31813     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31814     {
31815         var pos = [];
31816         
31817         if(box[0].size == 'md-left'){
31818             pos.push({
31819                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31820                 y : minY
31821             });
31822             
31823             return pos;
31824         }
31825         
31826         if(box[0].size == 'md-right'){
31827             pos.push({
31828                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31829                 y : minY + (this.unitWidth + this.gutter) * 1
31830             });
31831             
31832             return pos;
31833         }
31834         
31835         var rand = Math.floor(Math.random() * (4 - box[0].y));
31836         
31837         pos.push({
31838             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31839             y : minY + (this.unitWidth + this.gutter) * rand
31840         });
31841         
31842         return pos;
31843         
31844     },
31845     
31846     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31847     {
31848         var pos = [];
31849         
31850         if(box[0].size == 'xs'){
31851             
31852             pos.push({
31853                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31854                 y : minY
31855             });
31856
31857             pos.push({
31858                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31859                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31860             });
31861             
31862             return pos;
31863             
31864         }
31865         
31866         pos.push({
31867             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31868             y : minY
31869         });
31870
31871         pos.push({
31872             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31873             y : minY + (this.unitWidth + this.gutter) * 2
31874         });
31875         
31876         return pos;
31877         
31878     },
31879     
31880     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31881     {
31882         var pos = [];
31883         
31884         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31885             
31886             pos.push({
31887                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31888                 y : minY
31889             });
31890
31891             pos.push({
31892                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31893                 y : minY + (this.unitWidth + this.gutter) * 1
31894             });
31895             
31896             pos.push({
31897                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31898                 y : minY + (this.unitWidth + this.gutter) * 2
31899             });
31900             
31901             return pos;
31902             
31903         }
31904         
31905         if(box[0].size == 'xs' && box[1].size == 'xs'){
31906             
31907             pos.push({
31908                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31909                 y : minY
31910             });
31911
31912             pos.push({
31913                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31914                 y : minY
31915             });
31916             
31917             pos.push({
31918                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31919                 y : minY + (this.unitWidth + this.gutter) * 1
31920             });
31921             
31922             return pos;
31923             
31924         }
31925         
31926         pos.push({
31927             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31928             y : minY
31929         });
31930
31931         pos.push({
31932             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31933             y : minY + (this.unitWidth + this.gutter) * 2
31934         });
31935
31936         pos.push({
31937             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31938             y : minY + (this.unitWidth + this.gutter) * 2
31939         });
31940             
31941         return pos;
31942         
31943     },
31944     
31945     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31946     {
31947         var pos = [];
31948         
31949         if(box[0].size == 'xs'){
31950             
31951             pos.push({
31952                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31953                 y : minY
31954             });
31955
31956             pos.push({
31957                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31958                 y : minY
31959             });
31960             
31961             pos.push({
31962                 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),
31963                 y : minY
31964             });
31965             
31966             pos.push({
31967                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31968                 y : minY + (this.unitWidth + this.gutter) * 1
31969             });
31970             
31971             return pos;
31972             
31973         }
31974         
31975         pos.push({
31976             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31977             y : minY
31978         });
31979         
31980         pos.push({
31981             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31982             y : minY + (this.unitWidth + this.gutter) * 2
31983         });
31984         
31985         pos.push({
31986             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31987             y : minY + (this.unitWidth + this.gutter) * 2
31988         });
31989         
31990         pos.push({
31991             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),
31992             y : minY + (this.unitWidth + this.gutter) * 2
31993         });
31994
31995         return pos;
31996         
31997     },
31998     
31999     /**
32000     * remove a Masonry Brick
32001     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32002     */
32003     removeBrick : function(brick_id)
32004     {
32005         if (!brick_id) {
32006             return;
32007         }
32008         
32009         for (var i = 0; i<this.bricks.length; i++) {
32010             if (this.bricks[i].id == brick_id) {
32011                 this.bricks.splice(i,1);
32012                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32013                 this.initial();
32014             }
32015         }
32016     },
32017     
32018     /**
32019     * adds a Masonry Brick
32020     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32021     */
32022     addBrick : function(cfg)
32023     {
32024         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32025         //this.register(cn);
32026         cn.parentId = this.id;
32027         cn.render(this.el);
32028         return cn;
32029     },
32030     
32031     /**
32032     * register a Masonry Brick
32033     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32034     */
32035     
32036     register : function(brick)
32037     {
32038         this.bricks.push(brick);
32039         brick.masonryId = this.id;
32040     },
32041     
32042     /**
32043     * clear all the Masonry Brick
32044     */
32045     clearAll : function()
32046     {
32047         this.bricks = [];
32048         //this.getChildContainer().dom.innerHTML = "";
32049         this.el.dom.innerHTML = '';
32050     },
32051     
32052     getSelected : function()
32053     {
32054         if (!this.selectedBrick) {
32055             return false;
32056         }
32057         
32058         return this.selectedBrick;
32059     }
32060 });
32061
32062 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32063     
32064     groups: {},
32065      /**
32066     * register a Masonry Layout
32067     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32068     */
32069     
32070     register : function(layout)
32071     {
32072         this.groups[layout.id] = layout;
32073     },
32074     /**
32075     * fetch a  Masonry Layout based on the masonry layout ID
32076     * @param {string} the masonry layout to add
32077     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32078     */
32079     
32080     get: function(layout_id) {
32081         if (typeof(this.groups[layout_id]) == 'undefined') {
32082             return false;
32083         }
32084         return this.groups[layout_id] ;
32085     }
32086     
32087     
32088     
32089 });
32090
32091  
32092
32093  /**
32094  *
32095  * This is based on 
32096  * http://masonry.desandro.com
32097  *
32098  * The idea is to render all the bricks based on vertical width...
32099  *
32100  * The original code extends 'outlayer' - we might need to use that....
32101  * 
32102  */
32103
32104
32105 /**
32106  * @class Roo.bootstrap.LayoutMasonryAuto
32107  * @extends Roo.bootstrap.Component
32108  * Bootstrap Layout Masonry class
32109  * 
32110  * @constructor
32111  * Create a new Element
32112  * @param {Object} config The config object
32113  */
32114
32115 Roo.bootstrap.LayoutMasonryAuto = function(config){
32116     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32117 };
32118
32119 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32120     
32121       /**
32122      * @cfg {Boolean} isFitWidth  - resize the width..
32123      */   
32124     isFitWidth : false,  // options..
32125     /**
32126      * @cfg {Boolean} isOriginLeft = left align?
32127      */   
32128     isOriginLeft : true,
32129     /**
32130      * @cfg {Boolean} isOriginTop = top align?
32131      */   
32132     isOriginTop : false,
32133     /**
32134      * @cfg {Boolean} isLayoutInstant = no animation?
32135      */   
32136     isLayoutInstant : false, // needed?
32137     /**
32138      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32139      */   
32140     isResizingContainer : true,
32141     /**
32142      * @cfg {Number} columnWidth  width of the columns 
32143      */   
32144     
32145     columnWidth : 0,
32146     
32147     /**
32148      * @cfg {Number} maxCols maximum number of columns
32149      */   
32150     
32151     maxCols: 0,
32152     /**
32153      * @cfg {Number} padHeight padding below box..
32154      */   
32155     
32156     padHeight : 10, 
32157     
32158     /**
32159      * @cfg {Boolean} isAutoInitial defalut true
32160      */   
32161     
32162     isAutoInitial : true, 
32163     
32164     // private?
32165     gutter : 0,
32166     
32167     containerWidth: 0,
32168     initialColumnWidth : 0,
32169     currentSize : null,
32170     
32171     colYs : null, // array.
32172     maxY : 0,
32173     padWidth: 10,
32174     
32175     
32176     tag: 'div',
32177     cls: '',
32178     bricks: null, //CompositeElement
32179     cols : 0, // array?
32180     // element : null, // wrapped now this.el
32181     _isLayoutInited : null, 
32182     
32183     
32184     getAutoCreate : function(){
32185         
32186         var cfg = {
32187             tag: this.tag,
32188             cls: 'blog-masonary-wrapper ' + this.cls,
32189             cn : {
32190                 cls : 'mas-boxes masonary'
32191             }
32192         };
32193         
32194         return cfg;
32195     },
32196     
32197     getChildContainer: function( )
32198     {
32199         if (this.boxesEl) {
32200             return this.boxesEl;
32201         }
32202         
32203         this.boxesEl = this.el.select('.mas-boxes').first();
32204         
32205         return this.boxesEl;
32206     },
32207     
32208     
32209     initEvents : function()
32210     {
32211         var _this = this;
32212         
32213         if(this.isAutoInitial){
32214             Roo.log('hook children rendered');
32215             this.on('childrenrendered', function() {
32216                 Roo.log('children rendered');
32217                 _this.initial();
32218             } ,this);
32219         }
32220         
32221     },
32222     
32223     initial : function()
32224     {
32225         this.reloadItems();
32226
32227         this.currentSize = this.el.getBox(true);
32228
32229         /// was window resize... - let's see if this works..
32230         Roo.EventManager.onWindowResize(this.resize, this); 
32231
32232         if(!this.isAutoInitial){
32233             this.layout();
32234             return;
32235         }
32236         
32237         this.layout.defer(500,this);
32238     },
32239     
32240     reloadItems: function()
32241     {
32242         this.bricks = this.el.select('.masonry-brick', true);
32243         
32244         this.bricks.each(function(b) {
32245             //Roo.log(b.getSize());
32246             if (!b.attr('originalwidth')) {
32247                 b.attr('originalwidth',  b.getSize().width);
32248             }
32249             
32250         });
32251         
32252         Roo.log(this.bricks.elements.length);
32253     },
32254     
32255     resize : function()
32256     {
32257         Roo.log('resize');
32258         var cs = this.el.getBox(true);
32259         
32260         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32261             Roo.log("no change in with or X");
32262             return;
32263         }
32264         this.currentSize = cs;
32265         this.layout();
32266     },
32267     
32268     layout : function()
32269     {
32270          Roo.log('layout');
32271         this._resetLayout();
32272         //this._manageStamps();
32273       
32274         // don't animate first layout
32275         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32276         this.layoutItems( isInstant );
32277       
32278         // flag for initalized
32279         this._isLayoutInited = true;
32280     },
32281     
32282     layoutItems : function( isInstant )
32283     {
32284         //var items = this._getItemsForLayout( this.items );
32285         // original code supports filtering layout items.. we just ignore it..
32286         
32287         this._layoutItems( this.bricks , isInstant );
32288       
32289         this._postLayout();
32290     },
32291     _layoutItems : function ( items , isInstant)
32292     {
32293        //this.fireEvent( 'layout', this, items );
32294     
32295
32296         if ( !items || !items.elements.length ) {
32297           // no items, emit event with empty array
32298             return;
32299         }
32300
32301         var queue = [];
32302         items.each(function(item) {
32303             Roo.log("layout item");
32304             Roo.log(item);
32305             // get x/y object from method
32306             var position = this._getItemLayoutPosition( item );
32307             // enqueue
32308             position.item = item;
32309             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32310             queue.push( position );
32311         }, this);
32312       
32313         this._processLayoutQueue( queue );
32314     },
32315     /** Sets position of item in DOM
32316     * @param {Element} item
32317     * @param {Number} x - horizontal position
32318     * @param {Number} y - vertical position
32319     * @param {Boolean} isInstant - disables transitions
32320     */
32321     _processLayoutQueue : function( queue )
32322     {
32323         for ( var i=0, len = queue.length; i < len; i++ ) {
32324             var obj = queue[i];
32325             obj.item.position('absolute');
32326             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32327         }
32328     },
32329       
32330     
32331     /**
32332     * Any logic you want to do after each layout,
32333     * i.e. size the container
32334     */
32335     _postLayout : function()
32336     {
32337         this.resizeContainer();
32338     },
32339     
32340     resizeContainer : function()
32341     {
32342         if ( !this.isResizingContainer ) {
32343             return;
32344         }
32345         var size = this._getContainerSize();
32346         if ( size ) {
32347             this.el.setSize(size.width,size.height);
32348             this.boxesEl.setSize(size.width,size.height);
32349         }
32350     },
32351     
32352     
32353     
32354     _resetLayout : function()
32355     {
32356         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32357         this.colWidth = this.el.getWidth();
32358         //this.gutter = this.el.getWidth(); 
32359         
32360         this.measureColumns();
32361
32362         // reset column Y
32363         var i = this.cols;
32364         this.colYs = [];
32365         while (i--) {
32366             this.colYs.push( 0 );
32367         }
32368     
32369         this.maxY = 0;
32370     },
32371
32372     measureColumns : function()
32373     {
32374         this.getContainerWidth();
32375       // if columnWidth is 0, default to outerWidth of first item
32376         if ( !this.columnWidth ) {
32377             var firstItem = this.bricks.first();
32378             Roo.log(firstItem);
32379             this.columnWidth  = this.containerWidth;
32380             if (firstItem && firstItem.attr('originalwidth') ) {
32381                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32382             }
32383             // columnWidth fall back to item of first element
32384             Roo.log("set column width?");
32385                         this.initialColumnWidth = this.columnWidth  ;
32386
32387             // if first elem has no width, default to size of container
32388             
32389         }
32390         
32391         
32392         if (this.initialColumnWidth) {
32393             this.columnWidth = this.initialColumnWidth;
32394         }
32395         
32396         
32397             
32398         // column width is fixed at the top - however if container width get's smaller we should
32399         // reduce it...
32400         
32401         // this bit calcs how man columns..
32402             
32403         var columnWidth = this.columnWidth += this.gutter;
32404       
32405         // calculate columns
32406         var containerWidth = this.containerWidth + this.gutter;
32407         
32408         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32409         // fix rounding errors, typically with gutters
32410         var excess = columnWidth - containerWidth % columnWidth;
32411         
32412         
32413         // if overshoot is less than a pixel, round up, otherwise floor it
32414         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32415         cols = Math[ mathMethod ]( cols );
32416         this.cols = Math.max( cols, 1 );
32417         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32418         
32419          // padding positioning..
32420         var totalColWidth = this.cols * this.columnWidth;
32421         var padavail = this.containerWidth - totalColWidth;
32422         // so for 2 columns - we need 3 'pads'
32423         
32424         var padNeeded = (1+this.cols) * this.padWidth;
32425         
32426         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32427         
32428         this.columnWidth += padExtra
32429         //this.padWidth = Math.floor(padavail /  ( this.cols));
32430         
32431         // adjust colum width so that padding is fixed??
32432         
32433         // we have 3 columns ... total = width * 3
32434         // we have X left over... that should be used by 
32435         
32436         //if (this.expandC) {
32437             
32438         //}
32439         
32440         
32441         
32442     },
32443     
32444     getContainerWidth : function()
32445     {
32446        /* // container is parent if fit width
32447         var container = this.isFitWidth ? this.element.parentNode : this.element;
32448         // check that this.size and size are there
32449         // IE8 triggers resize on body size change, so they might not be
32450         
32451         var size = getSize( container );  //FIXME
32452         this.containerWidth = size && size.innerWidth; //FIXME
32453         */
32454          
32455         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32456         
32457     },
32458     
32459     _getItemLayoutPosition : function( item )  // what is item?
32460     {
32461         // we resize the item to our columnWidth..
32462       
32463         item.setWidth(this.columnWidth);
32464         item.autoBoxAdjust  = false;
32465         
32466         var sz = item.getSize();
32467  
32468         // how many columns does this brick span
32469         var remainder = this.containerWidth % this.columnWidth;
32470         
32471         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32472         // round if off by 1 pixel, otherwise use ceil
32473         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32474         colSpan = Math.min( colSpan, this.cols );
32475         
32476         // normally this should be '1' as we dont' currently allow multi width columns..
32477         
32478         var colGroup = this._getColGroup( colSpan );
32479         // get the minimum Y value from the columns
32480         var minimumY = Math.min.apply( Math, colGroup );
32481         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32482         
32483         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32484          
32485         // position the brick
32486         var position = {
32487             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32488             y: this.currentSize.y + minimumY + this.padHeight
32489         };
32490         
32491         Roo.log(position);
32492         // apply setHeight to necessary columns
32493         var setHeight = minimumY + sz.height + this.padHeight;
32494         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32495         
32496         var setSpan = this.cols + 1 - colGroup.length;
32497         for ( var i = 0; i < setSpan; i++ ) {
32498           this.colYs[ shortColIndex + i ] = setHeight ;
32499         }
32500       
32501         return position;
32502     },
32503     
32504     /**
32505      * @param {Number} colSpan - number of columns the element spans
32506      * @returns {Array} colGroup
32507      */
32508     _getColGroup : function( colSpan )
32509     {
32510         if ( colSpan < 2 ) {
32511           // if brick spans only one column, use all the column Ys
32512           return this.colYs;
32513         }
32514       
32515         var colGroup = [];
32516         // how many different places could this brick fit horizontally
32517         var groupCount = this.cols + 1 - colSpan;
32518         // for each group potential horizontal position
32519         for ( var i = 0; i < groupCount; i++ ) {
32520           // make an array of colY values for that one group
32521           var groupColYs = this.colYs.slice( i, i + colSpan );
32522           // and get the max value of the array
32523           colGroup[i] = Math.max.apply( Math, groupColYs );
32524         }
32525         return colGroup;
32526     },
32527     /*
32528     _manageStamp : function( stamp )
32529     {
32530         var stampSize =  stamp.getSize();
32531         var offset = stamp.getBox();
32532         // get the columns that this stamp affects
32533         var firstX = this.isOriginLeft ? offset.x : offset.right;
32534         var lastX = firstX + stampSize.width;
32535         var firstCol = Math.floor( firstX / this.columnWidth );
32536         firstCol = Math.max( 0, firstCol );
32537         
32538         var lastCol = Math.floor( lastX / this.columnWidth );
32539         // lastCol should not go over if multiple of columnWidth #425
32540         lastCol -= lastX % this.columnWidth ? 0 : 1;
32541         lastCol = Math.min( this.cols - 1, lastCol );
32542         
32543         // set colYs to bottom of the stamp
32544         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32545             stampSize.height;
32546             
32547         for ( var i = firstCol; i <= lastCol; i++ ) {
32548           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32549         }
32550     },
32551     */
32552     
32553     _getContainerSize : function()
32554     {
32555         this.maxY = Math.max.apply( Math, this.colYs );
32556         var size = {
32557             height: this.maxY
32558         };
32559       
32560         if ( this.isFitWidth ) {
32561             size.width = this._getContainerFitWidth();
32562         }
32563       
32564         return size;
32565     },
32566     
32567     _getContainerFitWidth : function()
32568     {
32569         var unusedCols = 0;
32570         // count unused columns
32571         var i = this.cols;
32572         while ( --i ) {
32573           if ( this.colYs[i] !== 0 ) {
32574             break;
32575           }
32576           unusedCols++;
32577         }
32578         // fit container to columns that have been used
32579         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32580     },
32581     
32582     needsResizeLayout : function()
32583     {
32584         var previousWidth = this.containerWidth;
32585         this.getContainerWidth();
32586         return previousWidth !== this.containerWidth;
32587     }
32588  
32589 });
32590
32591  
32592
32593  /*
32594  * - LGPL
32595  *
32596  * element
32597  * 
32598  */
32599
32600 /**
32601  * @class Roo.bootstrap.MasonryBrick
32602  * @extends Roo.bootstrap.Component
32603  * Bootstrap MasonryBrick class
32604  * 
32605  * @constructor
32606  * Create a new MasonryBrick
32607  * @param {Object} config The config object
32608  */
32609
32610 Roo.bootstrap.MasonryBrick = function(config){
32611     
32612     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32613     
32614     Roo.bootstrap.MasonryBrick.register(this);
32615     
32616     this.addEvents({
32617         // raw events
32618         /**
32619          * @event click
32620          * When a MasonryBrick is clcik
32621          * @param {Roo.bootstrap.MasonryBrick} this
32622          * @param {Roo.EventObject} e
32623          */
32624         "click" : true
32625     });
32626 };
32627
32628 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32629     
32630     /**
32631      * @cfg {String} title
32632      */   
32633     title : '',
32634     /**
32635      * @cfg {String} html
32636      */   
32637     html : '',
32638     /**
32639      * @cfg {String} bgimage
32640      */   
32641     bgimage : '',
32642     /**
32643      * @cfg {String} videourl
32644      */   
32645     videourl : '',
32646     /**
32647      * @cfg {String} cls
32648      */   
32649     cls : '',
32650     /**
32651      * @cfg {String} href
32652      */   
32653     href : '',
32654     /**
32655      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32656      */   
32657     size : 'xs',
32658     
32659     /**
32660      * @cfg {String} placetitle (center|bottom)
32661      */   
32662     placetitle : '',
32663     
32664     /**
32665      * @cfg {Boolean} isFitContainer defalut true
32666      */   
32667     isFitContainer : true, 
32668     
32669     /**
32670      * @cfg {Boolean} preventDefault defalut false
32671      */   
32672     preventDefault : false, 
32673     
32674     /**
32675      * @cfg {Boolean} inverse defalut false
32676      */   
32677     maskInverse : false, 
32678     
32679     getAutoCreate : function()
32680     {
32681         if(!this.isFitContainer){
32682             return this.getSplitAutoCreate();
32683         }
32684         
32685         var cls = 'masonry-brick masonry-brick-full';
32686         
32687         if(this.href.length){
32688             cls += ' masonry-brick-link';
32689         }
32690         
32691         if(this.bgimage.length){
32692             cls += ' masonry-brick-image';
32693         }
32694         
32695         if(this.maskInverse){
32696             cls += ' mask-inverse';
32697         }
32698         
32699         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32700             cls += ' enable-mask';
32701         }
32702         
32703         if(this.size){
32704             cls += ' masonry-' + this.size + '-brick';
32705         }
32706         
32707         if(this.placetitle.length){
32708             
32709             switch (this.placetitle) {
32710                 case 'center' :
32711                     cls += ' masonry-center-title';
32712                     break;
32713                 case 'bottom' :
32714                     cls += ' masonry-bottom-title';
32715                     break;
32716                 default:
32717                     break;
32718             }
32719             
32720         } else {
32721             if(!this.html.length && !this.bgimage.length){
32722                 cls += ' masonry-center-title';
32723             }
32724
32725             if(!this.html.length && this.bgimage.length){
32726                 cls += ' masonry-bottom-title';
32727             }
32728         }
32729         
32730         if(this.cls){
32731             cls += ' ' + this.cls;
32732         }
32733         
32734         var cfg = {
32735             tag: (this.href.length) ? 'a' : 'div',
32736             cls: cls,
32737             cn: [
32738                 {
32739                     tag: 'div',
32740                     cls: 'masonry-brick-mask'
32741                 },
32742                 {
32743                     tag: 'div',
32744                     cls: 'masonry-brick-paragraph',
32745                     cn: []
32746                 }
32747             ]
32748         };
32749         
32750         if(this.href.length){
32751             cfg.href = this.href;
32752         }
32753         
32754         var cn = cfg.cn[1].cn;
32755         
32756         if(this.title.length){
32757             cn.push({
32758                 tag: 'h4',
32759                 cls: 'masonry-brick-title',
32760                 html: this.title
32761             });
32762         }
32763         
32764         if(this.html.length){
32765             cn.push({
32766                 tag: 'p',
32767                 cls: 'masonry-brick-text',
32768                 html: this.html
32769             });
32770         }
32771         
32772         if (!this.title.length && !this.html.length) {
32773             cfg.cn[1].cls += ' hide';
32774         }
32775         
32776         if(this.bgimage.length){
32777             cfg.cn.push({
32778                 tag: 'img',
32779                 cls: 'masonry-brick-image-view',
32780                 src: this.bgimage
32781             });
32782         }
32783         
32784         if(this.videourl.length){
32785             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32786             // youtube support only?
32787             cfg.cn.push({
32788                 tag: 'iframe',
32789                 cls: 'masonry-brick-image-view',
32790                 src: vurl,
32791                 frameborder : 0,
32792                 allowfullscreen : true
32793             });
32794         }
32795         
32796         return cfg;
32797         
32798     },
32799     
32800     getSplitAutoCreate : function()
32801     {
32802         var cls = 'masonry-brick masonry-brick-split';
32803         
32804         if(this.href.length){
32805             cls += ' masonry-brick-link';
32806         }
32807         
32808         if(this.bgimage.length){
32809             cls += ' masonry-brick-image';
32810         }
32811         
32812         if(this.size){
32813             cls += ' masonry-' + this.size + '-brick';
32814         }
32815         
32816         switch (this.placetitle) {
32817             case 'center' :
32818                 cls += ' masonry-center-title';
32819                 break;
32820             case 'bottom' :
32821                 cls += ' masonry-bottom-title';
32822                 break;
32823             default:
32824                 if(!this.bgimage.length){
32825                     cls += ' masonry-center-title';
32826                 }
32827
32828                 if(this.bgimage.length){
32829                     cls += ' masonry-bottom-title';
32830                 }
32831                 break;
32832         }
32833         
32834         if(this.cls){
32835             cls += ' ' + this.cls;
32836         }
32837         
32838         var cfg = {
32839             tag: (this.href.length) ? 'a' : 'div',
32840             cls: cls,
32841             cn: [
32842                 {
32843                     tag: 'div',
32844                     cls: 'masonry-brick-split-head',
32845                     cn: [
32846                         {
32847                             tag: 'div',
32848                             cls: 'masonry-brick-paragraph',
32849                             cn: []
32850                         }
32851                     ]
32852                 },
32853                 {
32854                     tag: 'div',
32855                     cls: 'masonry-brick-split-body',
32856                     cn: []
32857                 }
32858             ]
32859         };
32860         
32861         if(this.href.length){
32862             cfg.href = this.href;
32863         }
32864         
32865         if(this.title.length){
32866             cfg.cn[0].cn[0].cn.push({
32867                 tag: 'h4',
32868                 cls: 'masonry-brick-title',
32869                 html: this.title
32870             });
32871         }
32872         
32873         if(this.html.length){
32874             cfg.cn[1].cn.push({
32875                 tag: 'p',
32876                 cls: 'masonry-brick-text',
32877                 html: this.html
32878             });
32879         }
32880
32881         if(this.bgimage.length){
32882             cfg.cn[0].cn.push({
32883                 tag: 'img',
32884                 cls: 'masonry-brick-image-view',
32885                 src: this.bgimage
32886             });
32887         }
32888         
32889         if(this.videourl.length){
32890             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32891             // youtube support only?
32892             cfg.cn[0].cn.cn.push({
32893                 tag: 'iframe',
32894                 cls: 'masonry-brick-image-view',
32895                 src: vurl,
32896                 frameborder : 0,
32897                 allowfullscreen : true
32898             });
32899         }
32900         
32901         return cfg;
32902     },
32903     
32904     initEvents: function() 
32905     {
32906         switch (this.size) {
32907             case 'xs' :
32908                 this.x = 1;
32909                 this.y = 1;
32910                 break;
32911             case 'sm' :
32912                 this.x = 2;
32913                 this.y = 2;
32914                 break;
32915             case 'md' :
32916             case 'md-left' :
32917             case 'md-right' :
32918                 this.x = 3;
32919                 this.y = 3;
32920                 break;
32921             case 'tall' :
32922                 this.x = 2;
32923                 this.y = 3;
32924                 break;
32925             case 'wide' :
32926                 this.x = 3;
32927                 this.y = 2;
32928                 break;
32929             case 'wide-thin' :
32930                 this.x = 3;
32931                 this.y = 1;
32932                 break;
32933                         
32934             default :
32935                 break;
32936         }
32937         
32938         if(Roo.isTouch){
32939             this.el.on('touchstart', this.onTouchStart, this);
32940             this.el.on('touchmove', this.onTouchMove, this);
32941             this.el.on('touchend', this.onTouchEnd, this);
32942             this.el.on('contextmenu', this.onContextMenu, this);
32943         } else {
32944             this.el.on('mouseenter'  ,this.enter, this);
32945             this.el.on('mouseleave', this.leave, this);
32946             this.el.on('click', this.onClick, this);
32947         }
32948         
32949         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32950             this.parent().bricks.push(this);   
32951         }
32952         
32953     },
32954     
32955     onClick: function(e, el)
32956     {
32957         var time = this.endTimer - this.startTimer;
32958         // Roo.log(e.preventDefault());
32959         if(Roo.isTouch){
32960             if(time > 1000){
32961                 e.preventDefault();
32962                 return;
32963             }
32964         }
32965         
32966         if(!this.preventDefault){
32967             return;
32968         }
32969         
32970         e.preventDefault();
32971         
32972         if (this.activeClass != '') {
32973             this.selectBrick();
32974         }
32975         
32976         this.fireEvent('click', this, e);
32977     },
32978     
32979     enter: function(e, el)
32980     {
32981         e.preventDefault();
32982         
32983         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32984             return;
32985         }
32986         
32987         if(this.bgimage.length && this.html.length){
32988             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32989         }
32990     },
32991     
32992     leave: function(e, el)
32993     {
32994         e.preventDefault();
32995         
32996         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32997             return;
32998         }
32999         
33000         if(this.bgimage.length && this.html.length){
33001             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33002         }
33003     },
33004     
33005     onTouchStart: function(e, el)
33006     {
33007 //        e.preventDefault();
33008         
33009         this.touchmoved = false;
33010         
33011         if(!this.isFitContainer){
33012             return;
33013         }
33014         
33015         if(!this.bgimage.length || !this.html.length){
33016             return;
33017         }
33018         
33019         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33020         
33021         this.timer = new Date().getTime();
33022         
33023     },
33024     
33025     onTouchMove: function(e, el)
33026     {
33027         this.touchmoved = true;
33028     },
33029     
33030     onContextMenu : function(e,el)
33031     {
33032         e.preventDefault();
33033         e.stopPropagation();
33034         return false;
33035     },
33036     
33037     onTouchEnd: function(e, el)
33038     {
33039 //        e.preventDefault();
33040         
33041         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33042         
33043             this.leave(e,el);
33044             
33045             return;
33046         }
33047         
33048         if(!this.bgimage.length || !this.html.length){
33049             
33050             if(this.href.length){
33051                 window.location.href = this.href;
33052             }
33053             
33054             return;
33055         }
33056         
33057         if(!this.isFitContainer){
33058             return;
33059         }
33060         
33061         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33062         
33063         window.location.href = this.href;
33064     },
33065     
33066     //selection on single brick only
33067     selectBrick : function() {
33068         
33069         if (!this.parentId) {
33070             return;
33071         }
33072         
33073         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33074         var index = m.selectedBrick.indexOf(this.id);
33075         
33076         if ( index > -1) {
33077             m.selectedBrick.splice(index,1);
33078             this.el.removeClass(this.activeClass);
33079             return;
33080         }
33081         
33082         for(var i = 0; i < m.selectedBrick.length; i++) {
33083             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33084             b.el.removeClass(b.activeClass);
33085         }
33086         
33087         m.selectedBrick = [];
33088         
33089         m.selectedBrick.push(this.id);
33090         this.el.addClass(this.activeClass);
33091         return;
33092     },
33093     
33094     isSelected : function(){
33095         return this.el.hasClass(this.activeClass);
33096         
33097     }
33098 });
33099
33100 Roo.apply(Roo.bootstrap.MasonryBrick, {
33101     
33102     //groups: {},
33103     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33104      /**
33105     * register a Masonry Brick
33106     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33107     */
33108     
33109     register : function(brick)
33110     {
33111         //this.groups[brick.id] = brick;
33112         this.groups.add(brick.id, brick);
33113     },
33114     /**
33115     * fetch a  masonry brick based on the masonry brick ID
33116     * @param {string} the masonry brick to add
33117     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33118     */
33119     
33120     get: function(brick_id) 
33121     {
33122         // if (typeof(this.groups[brick_id]) == 'undefined') {
33123         //     return false;
33124         // }
33125         // return this.groups[brick_id] ;
33126         
33127         if(this.groups.key(brick_id)) {
33128             return this.groups.key(brick_id);
33129         }
33130         
33131         return false;
33132     }
33133     
33134     
33135     
33136 });
33137
33138  /*
33139  * - LGPL
33140  *
33141  * element
33142  * 
33143  */
33144
33145 /**
33146  * @class Roo.bootstrap.Brick
33147  * @extends Roo.bootstrap.Component
33148  * Bootstrap Brick class
33149  * 
33150  * @constructor
33151  * Create a new Brick
33152  * @param {Object} config The config object
33153  */
33154
33155 Roo.bootstrap.Brick = function(config){
33156     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33157     
33158     this.addEvents({
33159         // raw events
33160         /**
33161          * @event click
33162          * When a Brick is click
33163          * @param {Roo.bootstrap.Brick} this
33164          * @param {Roo.EventObject} e
33165          */
33166         "click" : true
33167     });
33168 };
33169
33170 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33171     
33172     /**
33173      * @cfg {String} title
33174      */   
33175     title : '',
33176     /**
33177      * @cfg {String} html
33178      */   
33179     html : '',
33180     /**
33181      * @cfg {String} bgimage
33182      */   
33183     bgimage : '',
33184     /**
33185      * @cfg {String} cls
33186      */   
33187     cls : '',
33188     /**
33189      * @cfg {String} href
33190      */   
33191     href : '',
33192     /**
33193      * @cfg {String} video
33194      */   
33195     video : '',
33196     /**
33197      * @cfg {Boolean} square
33198      */   
33199     square : true,
33200     
33201     getAutoCreate : function()
33202     {
33203         var cls = 'roo-brick';
33204         
33205         if(this.href.length){
33206             cls += ' roo-brick-link';
33207         }
33208         
33209         if(this.bgimage.length){
33210             cls += ' roo-brick-image';
33211         }
33212         
33213         if(!this.html.length && !this.bgimage.length){
33214             cls += ' roo-brick-center-title';
33215         }
33216         
33217         if(!this.html.length && this.bgimage.length){
33218             cls += ' roo-brick-bottom-title';
33219         }
33220         
33221         if(this.cls){
33222             cls += ' ' + this.cls;
33223         }
33224         
33225         var cfg = {
33226             tag: (this.href.length) ? 'a' : 'div',
33227             cls: cls,
33228             cn: [
33229                 {
33230                     tag: 'div',
33231                     cls: 'roo-brick-paragraph',
33232                     cn: []
33233                 }
33234             ]
33235         };
33236         
33237         if(this.href.length){
33238             cfg.href = this.href;
33239         }
33240         
33241         var cn = cfg.cn[0].cn;
33242         
33243         if(this.title.length){
33244             cn.push({
33245                 tag: 'h4',
33246                 cls: 'roo-brick-title',
33247                 html: this.title
33248             });
33249         }
33250         
33251         if(this.html.length){
33252             cn.push({
33253                 tag: 'p',
33254                 cls: 'roo-brick-text',
33255                 html: this.html
33256             });
33257         } else {
33258             cn.cls += ' hide';
33259         }
33260         
33261         if(this.bgimage.length){
33262             cfg.cn.push({
33263                 tag: 'img',
33264                 cls: 'roo-brick-image-view',
33265                 src: this.bgimage
33266             });
33267         }
33268         
33269         return cfg;
33270     },
33271     
33272     initEvents: function() 
33273     {
33274         if(this.title.length || this.html.length){
33275             this.el.on('mouseenter'  ,this.enter, this);
33276             this.el.on('mouseleave', this.leave, this);
33277         }
33278         
33279         Roo.EventManager.onWindowResize(this.resize, this); 
33280         
33281         if(this.bgimage.length){
33282             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33283             this.imageEl.on('load', this.onImageLoad, this);
33284             return;
33285         }
33286         
33287         this.resize();
33288     },
33289     
33290     onImageLoad : function()
33291     {
33292         this.resize();
33293     },
33294     
33295     resize : function()
33296     {
33297         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33298         
33299         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33300         
33301         if(this.bgimage.length){
33302             var image = this.el.select('.roo-brick-image-view', true).first();
33303             
33304             image.setWidth(paragraph.getWidth());
33305             
33306             if(this.square){
33307                 image.setHeight(paragraph.getWidth());
33308             }
33309             
33310             this.el.setHeight(image.getHeight());
33311             paragraph.setHeight(image.getHeight());
33312             
33313         }
33314         
33315     },
33316     
33317     enter: function(e, el)
33318     {
33319         e.preventDefault();
33320         
33321         if(this.bgimage.length){
33322             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33323             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33324         }
33325     },
33326     
33327     leave: function(e, el)
33328     {
33329         e.preventDefault();
33330         
33331         if(this.bgimage.length){
33332             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33333             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33334         }
33335     }
33336     
33337 });
33338
33339  
33340
33341  /*
33342  * - LGPL
33343  *
33344  * Number field 
33345  */
33346
33347 /**
33348  * @class Roo.bootstrap.NumberField
33349  * @extends Roo.bootstrap.Input
33350  * Bootstrap NumberField class
33351  * 
33352  * 
33353  * 
33354  * 
33355  * @constructor
33356  * Create a new NumberField
33357  * @param {Object} config The config object
33358  */
33359
33360 Roo.bootstrap.NumberField = function(config){
33361     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33362 };
33363
33364 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33365     
33366     /**
33367      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33368      */
33369     allowDecimals : true,
33370     /**
33371      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33372      */
33373     decimalSeparator : ".",
33374     /**
33375      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33376      */
33377     decimalPrecision : 2,
33378     /**
33379      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33380      */
33381     allowNegative : true,
33382     
33383     /**
33384      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33385      */
33386     allowZero: true,
33387     /**
33388      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33389      */
33390     minValue : Number.NEGATIVE_INFINITY,
33391     /**
33392      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33393      */
33394     maxValue : Number.MAX_VALUE,
33395     /**
33396      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33397      */
33398     minText : "The minimum value for this field is {0}",
33399     /**
33400      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33401      */
33402     maxText : "The maximum value for this field is {0}",
33403     /**
33404      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33405      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33406      */
33407     nanText : "{0} is not a valid number",
33408     /**
33409      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33410      */
33411     thousandsDelimiter : false,
33412     /**
33413      * @cfg {String} valueAlign alignment of value
33414      */
33415     valueAlign : "left",
33416
33417     getAutoCreate : function()
33418     {
33419         var hiddenInput = {
33420             tag: 'input',
33421             type: 'hidden',
33422             id: Roo.id(),
33423             cls: 'hidden-number-input'
33424         };
33425         
33426         if (this.name) {
33427             hiddenInput.name = this.name;
33428         }
33429         
33430         this.name = '';
33431         
33432         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33433         
33434         this.name = hiddenInput.name;
33435         
33436         if(cfg.cn.length > 0) {
33437             cfg.cn.push(hiddenInput);
33438         }
33439         
33440         return cfg;
33441     },
33442
33443     // private
33444     initEvents : function()
33445     {   
33446         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33447         
33448         var allowed = "0123456789";
33449         
33450         if(this.allowDecimals){
33451             allowed += this.decimalSeparator;
33452         }
33453         
33454         if(this.allowNegative){
33455             allowed += "-";
33456         }
33457         
33458         if(this.thousandsDelimiter) {
33459             allowed += ",";
33460         }
33461         
33462         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33463         
33464         var keyPress = function(e){
33465             
33466             var k = e.getKey();
33467             
33468             var c = e.getCharCode();
33469             
33470             if(
33471                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33472                     allowed.indexOf(String.fromCharCode(c)) === -1
33473             ){
33474                 e.stopEvent();
33475                 return;
33476             }
33477             
33478             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33479                 return;
33480             }
33481             
33482             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33483                 e.stopEvent();
33484             }
33485         };
33486         
33487         this.el.on("keypress", keyPress, this);
33488     },
33489     
33490     validateValue : function(value)
33491     {
33492         
33493         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33494             return false;
33495         }
33496         
33497         var num = this.parseValue(value);
33498         
33499         if(isNaN(num)){
33500             this.markInvalid(String.format(this.nanText, value));
33501             return false;
33502         }
33503         
33504         if(num < this.minValue){
33505             this.markInvalid(String.format(this.minText, this.minValue));
33506             return false;
33507         }
33508         
33509         if(num > this.maxValue){
33510             this.markInvalid(String.format(this.maxText, this.maxValue));
33511             return false;
33512         }
33513         
33514         return true;
33515     },
33516
33517     getValue : function()
33518     {
33519         var v = this.hiddenEl().getValue();
33520         
33521         return this.fixPrecision(this.parseValue(v));
33522     },
33523
33524     parseValue : function(value)
33525     {
33526         if(this.thousandsDelimiter) {
33527             value += "";
33528             r = new RegExp(",", "g");
33529             value = value.replace(r, "");
33530         }
33531         
33532         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33533         return isNaN(value) ? '' : value;
33534     },
33535
33536     fixPrecision : function(value)
33537     {
33538         if(this.thousandsDelimiter) {
33539             value += "";
33540             r = new RegExp(",", "g");
33541             value = value.replace(r, "");
33542         }
33543         
33544         var nan = isNaN(value);
33545         
33546         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33547             return nan ? '' : value;
33548         }
33549         return parseFloat(value).toFixed(this.decimalPrecision);
33550     },
33551
33552     setValue : function(v)
33553     {
33554         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33555         
33556         this.value = v;
33557         
33558         if(this.rendered){
33559             
33560             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33561             
33562             this.inputEl().dom.value = (v == '') ? '' :
33563                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33564             
33565             if(!this.allowZero && v === '0') {
33566                 this.hiddenEl().dom.value = '';
33567                 this.inputEl().dom.value = '';
33568             }
33569             
33570             this.validate();
33571         }
33572     },
33573
33574     decimalPrecisionFcn : function(v)
33575     {
33576         return Math.floor(v);
33577     },
33578
33579     beforeBlur : function()
33580     {
33581         var v = this.parseValue(this.getRawValue());
33582         
33583         if(v || v === 0 || v === ''){
33584             this.setValue(v);
33585         }
33586     },
33587     
33588     hiddenEl : function()
33589     {
33590         return this.el.select('input.hidden-number-input',true).first();
33591     }
33592     
33593 });
33594
33595  
33596
33597 /*
33598 * Licence: LGPL
33599 */
33600
33601 /**
33602  * @class Roo.bootstrap.DocumentSlider
33603  * @extends Roo.bootstrap.Component
33604  * Bootstrap DocumentSlider class
33605  * 
33606  * @constructor
33607  * Create a new DocumentViewer
33608  * @param {Object} config The config object
33609  */
33610
33611 Roo.bootstrap.DocumentSlider = function(config){
33612     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33613     
33614     this.files = [];
33615     
33616     this.addEvents({
33617         /**
33618          * @event initial
33619          * Fire after initEvent
33620          * @param {Roo.bootstrap.DocumentSlider} this
33621          */
33622         "initial" : true,
33623         /**
33624          * @event update
33625          * Fire after update
33626          * @param {Roo.bootstrap.DocumentSlider} this
33627          */
33628         "update" : true,
33629         /**
33630          * @event click
33631          * Fire after click
33632          * @param {Roo.bootstrap.DocumentSlider} this
33633          */
33634         "click" : true
33635     });
33636 };
33637
33638 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33639     
33640     files : false,
33641     
33642     indicator : 0,
33643     
33644     getAutoCreate : function()
33645     {
33646         var cfg = {
33647             tag : 'div',
33648             cls : 'roo-document-slider',
33649             cn : [
33650                 {
33651                     tag : 'div',
33652                     cls : 'roo-document-slider-header',
33653                     cn : [
33654                         {
33655                             tag : 'div',
33656                             cls : 'roo-document-slider-header-title'
33657                         }
33658                     ]
33659                 },
33660                 {
33661                     tag : 'div',
33662                     cls : 'roo-document-slider-body',
33663                     cn : [
33664                         {
33665                             tag : 'div',
33666                             cls : 'roo-document-slider-prev',
33667                             cn : [
33668                                 {
33669                                     tag : 'i',
33670                                     cls : 'fa fa-chevron-left'
33671                                 }
33672                             ]
33673                         },
33674                         {
33675                             tag : 'div',
33676                             cls : 'roo-document-slider-thumb',
33677                             cn : [
33678                                 {
33679                                     tag : 'img',
33680                                     cls : 'roo-document-slider-image'
33681                                 }
33682                             ]
33683                         },
33684                         {
33685                             tag : 'div',
33686                             cls : 'roo-document-slider-next',
33687                             cn : [
33688                                 {
33689                                     tag : 'i',
33690                                     cls : 'fa fa-chevron-right'
33691                                 }
33692                             ]
33693                         }
33694                     ]
33695                 }
33696             ]
33697         };
33698         
33699         return cfg;
33700     },
33701     
33702     initEvents : function()
33703     {
33704         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33705         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33706         
33707         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33708         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33709         
33710         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33711         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33712         
33713         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33714         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33715         
33716         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33717         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33718         
33719         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33720         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33721         
33722         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33723         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33724         
33725         this.thumbEl.on('click', this.onClick, this);
33726         
33727         this.prevIndicator.on('click', this.prev, this);
33728         
33729         this.nextIndicator.on('click', this.next, this);
33730         
33731     },
33732     
33733     initial : function()
33734     {
33735         if(this.files.length){
33736             this.indicator = 1;
33737             this.update()
33738         }
33739         
33740         this.fireEvent('initial', this);
33741     },
33742     
33743     update : function()
33744     {
33745         this.imageEl.attr('src', this.files[this.indicator - 1]);
33746         
33747         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33748         
33749         this.prevIndicator.show();
33750         
33751         if(this.indicator == 1){
33752             this.prevIndicator.hide();
33753         }
33754         
33755         this.nextIndicator.show();
33756         
33757         if(this.indicator == this.files.length){
33758             this.nextIndicator.hide();
33759         }
33760         
33761         this.thumbEl.scrollTo('top');
33762         
33763         this.fireEvent('update', this);
33764     },
33765     
33766     onClick : function(e)
33767     {
33768         e.preventDefault();
33769         
33770         this.fireEvent('click', this);
33771     },
33772     
33773     prev : function(e)
33774     {
33775         e.preventDefault();
33776         
33777         this.indicator = Math.max(1, this.indicator - 1);
33778         
33779         this.update();
33780     },
33781     
33782     next : function(e)
33783     {
33784         e.preventDefault();
33785         
33786         this.indicator = Math.min(this.files.length, this.indicator + 1);
33787         
33788         this.update();
33789     }
33790 });
33791 /*
33792  * - LGPL
33793  *
33794  * RadioSet
33795  *
33796  *
33797  */
33798
33799 /**
33800  * @class Roo.bootstrap.RadioSet
33801  * @extends Roo.bootstrap.Input
33802  * Bootstrap RadioSet class
33803  * @cfg {String} indicatorpos (left|right) default left
33804  * @cfg {Boolean} inline (true|false) inline the element (default true)
33805  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33806  * @constructor
33807  * Create a new RadioSet
33808  * @param {Object} config The config object
33809  */
33810
33811 Roo.bootstrap.RadioSet = function(config){
33812     
33813     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33814     
33815     this.radioes = [];
33816     
33817     Roo.bootstrap.RadioSet.register(this);
33818     
33819     this.addEvents({
33820         /**
33821         * @event check
33822         * Fires when the element is checked or unchecked.
33823         * @param {Roo.bootstrap.RadioSet} this This radio
33824         * @param {Roo.bootstrap.Radio} item The checked item
33825         */
33826        check : true,
33827        /**
33828         * @event click
33829         * Fires when the element is click.
33830         * @param {Roo.bootstrap.RadioSet} this This radio set
33831         * @param {Roo.bootstrap.Radio} item The checked item
33832         * @param {Roo.EventObject} e The event object
33833         */
33834        click : true
33835     });
33836     
33837 };
33838
33839 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33840
33841     radioes : false,
33842     
33843     inline : true,
33844     
33845     weight : '',
33846     
33847     indicatorpos : 'left',
33848     
33849     getAutoCreate : function()
33850     {
33851         var label = {
33852             tag : 'label',
33853             cls : 'roo-radio-set-label',
33854             cn : [
33855                 {
33856                     tag : 'span',
33857                     html : this.fieldLabel
33858                 }
33859             ]
33860         };
33861         
33862         if(this.indicatorpos == 'left'){
33863             label.cn.unshift({
33864                 tag : 'i',
33865                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33866                 tooltip : 'This field is required'
33867             });
33868         } else {
33869             label.cn.push({
33870                 tag : 'i',
33871                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33872                 tooltip : 'This field is required'
33873             });
33874         }
33875         
33876         var items = {
33877             tag : 'div',
33878             cls : 'roo-radio-set-items'
33879         };
33880         
33881         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33882         
33883         if (align === 'left' && this.fieldLabel.length) {
33884             
33885             items = {
33886                 cls : "roo-radio-set-right", 
33887                 cn: [
33888                     items
33889                 ]
33890             };
33891             
33892             if(this.labelWidth > 12){
33893                 label.style = "width: " + this.labelWidth + 'px';
33894             }
33895             
33896             if(this.labelWidth < 13 && this.labelmd == 0){
33897                 this.labelmd = this.labelWidth;
33898             }
33899             
33900             if(this.labellg > 0){
33901                 label.cls += ' col-lg-' + this.labellg;
33902                 items.cls += ' col-lg-' + (12 - this.labellg);
33903             }
33904             
33905             if(this.labelmd > 0){
33906                 label.cls += ' col-md-' + this.labelmd;
33907                 items.cls += ' col-md-' + (12 - this.labelmd);
33908             }
33909             
33910             if(this.labelsm > 0){
33911                 label.cls += ' col-sm-' + this.labelsm;
33912                 items.cls += ' col-sm-' + (12 - this.labelsm);
33913             }
33914             
33915             if(this.labelxs > 0){
33916                 label.cls += ' col-xs-' + this.labelxs;
33917                 items.cls += ' col-xs-' + (12 - this.labelxs);
33918             }
33919         }
33920         
33921         var cfg = {
33922             tag : 'div',
33923             cls : 'roo-radio-set',
33924             cn : [
33925                 {
33926                     tag : 'input',
33927                     cls : 'roo-radio-set-input',
33928                     type : 'hidden',
33929                     name : this.name,
33930                     value : this.value ? this.value :  ''
33931                 },
33932                 label,
33933                 items
33934             ]
33935         };
33936         
33937         if(this.weight.length){
33938             cfg.cls += ' roo-radio-' + this.weight;
33939         }
33940         
33941         if(this.inline) {
33942             cfg.cls += ' roo-radio-set-inline';
33943         }
33944         
33945         var settings=this;
33946         ['xs','sm','md','lg'].map(function(size){
33947             if (settings[size]) {
33948                 cfg.cls += ' col-' + size + '-' + settings[size];
33949             }
33950         });
33951         
33952         return cfg;
33953         
33954     },
33955
33956     initEvents : function()
33957     {
33958         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33959         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33960         
33961         if(!this.fieldLabel.length){
33962             this.labelEl.hide();
33963         }
33964         
33965         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33966         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33967         
33968         this.indicator = this.indicatorEl();
33969         
33970         if(this.indicator){
33971             this.indicator.addClass('invisible');
33972         }
33973         
33974         this.originalValue = this.getValue();
33975         
33976     },
33977     
33978     inputEl: function ()
33979     {
33980         return this.el.select('.roo-radio-set-input', true).first();
33981     },
33982     
33983     getChildContainer : function()
33984     {
33985         return this.itemsEl;
33986     },
33987     
33988     register : function(item)
33989     {
33990         this.radioes.push(item);
33991         
33992     },
33993     
33994     validate : function()
33995     {   
33996         if(this.getVisibilityEl().hasClass('hidden')){
33997             return true;
33998         }
33999         
34000         var valid = false;
34001         
34002         Roo.each(this.radioes, function(i){
34003             if(!i.checked){
34004                 return;
34005             }
34006             
34007             valid = true;
34008             return false;
34009         });
34010         
34011         if(this.allowBlank) {
34012             return true;
34013         }
34014         
34015         if(this.disabled || valid){
34016             this.markValid();
34017             return true;
34018         }
34019         
34020         this.markInvalid();
34021         return false;
34022         
34023     },
34024     
34025     markValid : function()
34026     {
34027         if(this.labelEl.isVisible(true)){
34028             this.indicatorEl().removeClass('visible');
34029             this.indicatorEl().addClass('invisible');
34030         }
34031         
34032         this.el.removeClass([this.invalidClass, this.validClass]);
34033         this.el.addClass(this.validClass);
34034         
34035         this.fireEvent('valid', this);
34036     },
34037     
34038     markInvalid : function(msg)
34039     {
34040         if(this.allowBlank || this.disabled){
34041             return;
34042         }
34043         
34044         if(this.labelEl.isVisible(true)){
34045             this.indicatorEl().removeClass('invisible');
34046             this.indicatorEl().addClass('visible');
34047         }
34048         
34049         this.el.removeClass([this.invalidClass, this.validClass]);
34050         this.el.addClass(this.invalidClass);
34051         
34052         this.fireEvent('invalid', this, msg);
34053         
34054     },
34055     
34056     setValue : function(v, suppressEvent)
34057     {   
34058         if(this.value === v){
34059             return;
34060         }
34061         
34062         this.value = v;
34063         
34064         if(this.rendered){
34065             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34066         }
34067         
34068         Roo.each(this.radioes, function(i){
34069             i.checked = false;
34070             i.el.removeClass('checked');
34071         });
34072         
34073         Roo.each(this.radioes, function(i){
34074             
34075             if(i.value === v || i.value.toString() === v.toString()){
34076                 i.checked = true;
34077                 i.el.addClass('checked');
34078                 
34079                 if(suppressEvent !== true){
34080                     this.fireEvent('check', this, i);
34081                 }
34082                 
34083                 return false;
34084             }
34085             
34086         }, this);
34087         
34088         this.validate();
34089     },
34090     
34091     clearInvalid : function(){
34092         
34093         if(!this.el || this.preventMark){
34094             return;
34095         }
34096         
34097         this.el.removeClass([this.invalidClass]);
34098         
34099         this.fireEvent('valid', this);
34100     }
34101     
34102 });
34103
34104 Roo.apply(Roo.bootstrap.RadioSet, {
34105     
34106     groups: {},
34107     
34108     register : function(set)
34109     {
34110         this.groups[set.name] = set;
34111     },
34112     
34113     get: function(name) 
34114     {
34115         if (typeof(this.groups[name]) == 'undefined') {
34116             return false;
34117         }
34118         
34119         return this.groups[name] ;
34120     }
34121     
34122 });
34123 /*
34124  * Based on:
34125  * Ext JS Library 1.1.1
34126  * Copyright(c) 2006-2007, Ext JS, LLC.
34127  *
34128  * Originally Released Under LGPL - original licence link has changed is not relivant.
34129  *
34130  * Fork - LGPL
34131  * <script type="text/javascript">
34132  */
34133
34134
34135 /**
34136  * @class Roo.bootstrap.SplitBar
34137  * @extends Roo.util.Observable
34138  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34139  * <br><br>
34140  * Usage:
34141  * <pre><code>
34142 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34143                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34144 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34145 split.minSize = 100;
34146 split.maxSize = 600;
34147 split.animate = true;
34148 split.on('moved', splitterMoved);
34149 </code></pre>
34150  * @constructor
34151  * Create a new SplitBar
34152  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34153  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34154  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34155  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34156                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34157                         position of the SplitBar).
34158  */
34159 Roo.bootstrap.SplitBar = function(cfg){
34160     
34161     /** @private */
34162     
34163     //{
34164     //  dragElement : elm
34165     //  resizingElement: el,
34166         // optional..
34167     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34168     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34169         // existingProxy ???
34170     //}
34171     
34172     this.el = Roo.get(cfg.dragElement, true);
34173     this.el.dom.unselectable = "on";
34174     /** @private */
34175     this.resizingEl = Roo.get(cfg.resizingElement, true);
34176
34177     /**
34178      * @private
34179      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34180      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34181      * @type Number
34182      */
34183     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34184     
34185     /**
34186      * The minimum size of the resizing element. (Defaults to 0)
34187      * @type Number
34188      */
34189     this.minSize = 0;
34190     
34191     /**
34192      * The maximum size of the resizing element. (Defaults to 2000)
34193      * @type Number
34194      */
34195     this.maxSize = 2000;
34196     
34197     /**
34198      * Whether to animate the transition to the new size
34199      * @type Boolean
34200      */
34201     this.animate = false;
34202     
34203     /**
34204      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34205      * @type Boolean
34206      */
34207     this.useShim = false;
34208     
34209     /** @private */
34210     this.shim = null;
34211     
34212     if(!cfg.existingProxy){
34213         /** @private */
34214         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34215     }else{
34216         this.proxy = Roo.get(cfg.existingProxy).dom;
34217     }
34218     /** @private */
34219     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34220     
34221     /** @private */
34222     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34223     
34224     /** @private */
34225     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34226     
34227     /** @private */
34228     this.dragSpecs = {};
34229     
34230     /**
34231      * @private The adapter to use to positon and resize elements
34232      */
34233     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34234     this.adapter.init(this);
34235     
34236     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34237         /** @private */
34238         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34239         this.el.addClass("roo-splitbar-h");
34240     }else{
34241         /** @private */
34242         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34243         this.el.addClass("roo-splitbar-v");
34244     }
34245     
34246     this.addEvents({
34247         /**
34248          * @event resize
34249          * Fires when the splitter is moved (alias for {@link #event-moved})
34250          * @param {Roo.bootstrap.SplitBar} this
34251          * @param {Number} newSize the new width or height
34252          */
34253         "resize" : true,
34254         /**
34255          * @event moved
34256          * Fires when the splitter is moved
34257          * @param {Roo.bootstrap.SplitBar} this
34258          * @param {Number} newSize the new width or height
34259          */
34260         "moved" : true,
34261         /**
34262          * @event beforeresize
34263          * Fires before the splitter is dragged
34264          * @param {Roo.bootstrap.SplitBar} this
34265          */
34266         "beforeresize" : true,
34267
34268         "beforeapply" : true
34269     });
34270
34271     Roo.util.Observable.call(this);
34272 };
34273
34274 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34275     onStartProxyDrag : function(x, y){
34276         this.fireEvent("beforeresize", this);
34277         if(!this.overlay){
34278             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34279             o.unselectable();
34280             o.enableDisplayMode("block");
34281             // all splitbars share the same overlay
34282             Roo.bootstrap.SplitBar.prototype.overlay = o;
34283         }
34284         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34285         this.overlay.show();
34286         Roo.get(this.proxy).setDisplayed("block");
34287         var size = this.adapter.getElementSize(this);
34288         this.activeMinSize = this.getMinimumSize();;
34289         this.activeMaxSize = this.getMaximumSize();;
34290         var c1 = size - this.activeMinSize;
34291         var c2 = Math.max(this.activeMaxSize - size, 0);
34292         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34293             this.dd.resetConstraints();
34294             this.dd.setXConstraint(
34295                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34296                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34297             );
34298             this.dd.setYConstraint(0, 0);
34299         }else{
34300             this.dd.resetConstraints();
34301             this.dd.setXConstraint(0, 0);
34302             this.dd.setYConstraint(
34303                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34304                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34305             );
34306          }
34307         this.dragSpecs.startSize = size;
34308         this.dragSpecs.startPoint = [x, y];
34309         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34310     },
34311     
34312     /** 
34313      * @private Called after the drag operation by the DDProxy
34314      */
34315     onEndProxyDrag : function(e){
34316         Roo.get(this.proxy).setDisplayed(false);
34317         var endPoint = Roo.lib.Event.getXY(e);
34318         if(this.overlay){
34319             this.overlay.hide();
34320         }
34321         var newSize;
34322         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34323             newSize = this.dragSpecs.startSize + 
34324                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34325                     endPoint[0] - this.dragSpecs.startPoint[0] :
34326                     this.dragSpecs.startPoint[0] - endPoint[0]
34327                 );
34328         }else{
34329             newSize = this.dragSpecs.startSize + 
34330                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34331                     endPoint[1] - this.dragSpecs.startPoint[1] :
34332                     this.dragSpecs.startPoint[1] - endPoint[1]
34333                 );
34334         }
34335         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34336         if(newSize != this.dragSpecs.startSize){
34337             if(this.fireEvent('beforeapply', this, newSize) !== false){
34338                 this.adapter.setElementSize(this, newSize);
34339                 this.fireEvent("moved", this, newSize);
34340                 this.fireEvent("resize", this, newSize);
34341             }
34342         }
34343     },
34344     
34345     /**
34346      * Get the adapter this SplitBar uses
34347      * @return The adapter object
34348      */
34349     getAdapter : function(){
34350         return this.adapter;
34351     },
34352     
34353     /**
34354      * Set the adapter this SplitBar uses
34355      * @param {Object} adapter A SplitBar adapter object
34356      */
34357     setAdapter : function(adapter){
34358         this.adapter = adapter;
34359         this.adapter.init(this);
34360     },
34361     
34362     /**
34363      * Gets the minimum size for the resizing element
34364      * @return {Number} The minimum size
34365      */
34366     getMinimumSize : function(){
34367         return this.minSize;
34368     },
34369     
34370     /**
34371      * Sets the minimum size for the resizing element
34372      * @param {Number} minSize The minimum size
34373      */
34374     setMinimumSize : function(minSize){
34375         this.minSize = minSize;
34376     },
34377     
34378     /**
34379      * Gets the maximum size for the resizing element
34380      * @return {Number} The maximum size
34381      */
34382     getMaximumSize : function(){
34383         return this.maxSize;
34384     },
34385     
34386     /**
34387      * Sets the maximum size for the resizing element
34388      * @param {Number} maxSize The maximum size
34389      */
34390     setMaximumSize : function(maxSize){
34391         this.maxSize = maxSize;
34392     },
34393     
34394     /**
34395      * Sets the initialize size for the resizing element
34396      * @param {Number} size The initial size
34397      */
34398     setCurrentSize : function(size){
34399         var oldAnimate = this.animate;
34400         this.animate = false;
34401         this.adapter.setElementSize(this, size);
34402         this.animate = oldAnimate;
34403     },
34404     
34405     /**
34406      * Destroy this splitbar. 
34407      * @param {Boolean} removeEl True to remove the element
34408      */
34409     destroy : function(removeEl){
34410         if(this.shim){
34411             this.shim.remove();
34412         }
34413         this.dd.unreg();
34414         this.proxy.parentNode.removeChild(this.proxy);
34415         if(removeEl){
34416             this.el.remove();
34417         }
34418     }
34419 });
34420
34421 /**
34422  * @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.
34423  */
34424 Roo.bootstrap.SplitBar.createProxy = function(dir){
34425     var proxy = new Roo.Element(document.createElement("div"));
34426     proxy.unselectable();
34427     var cls = 'roo-splitbar-proxy';
34428     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34429     document.body.appendChild(proxy.dom);
34430     return proxy.dom;
34431 };
34432
34433 /** 
34434  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34435  * Default Adapter. It assumes the splitter and resizing element are not positioned
34436  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34437  */
34438 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34439 };
34440
34441 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34442     // do nothing for now
34443     init : function(s){
34444     
34445     },
34446     /**
34447      * Called before drag operations to get the current size of the resizing element. 
34448      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34449      */
34450      getElementSize : function(s){
34451         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34452             return s.resizingEl.getWidth();
34453         }else{
34454             return s.resizingEl.getHeight();
34455         }
34456     },
34457     
34458     /**
34459      * Called after drag operations to set the size of the resizing element.
34460      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34461      * @param {Number} newSize The new size to set
34462      * @param {Function} onComplete A function to be invoked when resizing is complete
34463      */
34464     setElementSize : function(s, newSize, onComplete){
34465         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34466             if(!s.animate){
34467                 s.resizingEl.setWidth(newSize);
34468                 if(onComplete){
34469                     onComplete(s, newSize);
34470                 }
34471             }else{
34472                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34473             }
34474         }else{
34475             
34476             if(!s.animate){
34477                 s.resizingEl.setHeight(newSize);
34478                 if(onComplete){
34479                     onComplete(s, newSize);
34480                 }
34481             }else{
34482                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34483             }
34484         }
34485     }
34486 };
34487
34488 /** 
34489  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34490  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34491  * Adapter that  moves the splitter element to align with the resized sizing element. 
34492  * Used with an absolute positioned SplitBar.
34493  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34494  * document.body, make sure you assign an id to the body element.
34495  */
34496 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34497     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34498     this.container = Roo.get(container);
34499 };
34500
34501 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34502     init : function(s){
34503         this.basic.init(s);
34504     },
34505     
34506     getElementSize : function(s){
34507         return this.basic.getElementSize(s);
34508     },
34509     
34510     setElementSize : function(s, newSize, onComplete){
34511         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34512     },
34513     
34514     moveSplitter : function(s){
34515         var yes = Roo.bootstrap.SplitBar;
34516         switch(s.placement){
34517             case yes.LEFT:
34518                 s.el.setX(s.resizingEl.getRight());
34519                 break;
34520             case yes.RIGHT:
34521                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34522                 break;
34523             case yes.TOP:
34524                 s.el.setY(s.resizingEl.getBottom());
34525                 break;
34526             case yes.BOTTOM:
34527                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34528                 break;
34529         }
34530     }
34531 };
34532
34533 /**
34534  * Orientation constant - Create a vertical SplitBar
34535  * @static
34536  * @type Number
34537  */
34538 Roo.bootstrap.SplitBar.VERTICAL = 1;
34539
34540 /**
34541  * Orientation constant - Create a horizontal SplitBar
34542  * @static
34543  * @type Number
34544  */
34545 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34546
34547 /**
34548  * Placement constant - The resizing element is to the left of the splitter element
34549  * @static
34550  * @type Number
34551  */
34552 Roo.bootstrap.SplitBar.LEFT = 1;
34553
34554 /**
34555  * Placement constant - The resizing element is to the right of the splitter element
34556  * @static
34557  * @type Number
34558  */
34559 Roo.bootstrap.SplitBar.RIGHT = 2;
34560
34561 /**
34562  * Placement constant - The resizing element is positioned above the splitter element
34563  * @static
34564  * @type Number
34565  */
34566 Roo.bootstrap.SplitBar.TOP = 3;
34567
34568 /**
34569  * Placement constant - The resizing element is positioned under splitter element
34570  * @static
34571  * @type Number
34572  */
34573 Roo.bootstrap.SplitBar.BOTTOM = 4;
34574 Roo.namespace("Roo.bootstrap.layout");/*
34575  * Based on:
34576  * Ext JS Library 1.1.1
34577  * Copyright(c) 2006-2007, Ext JS, LLC.
34578  *
34579  * Originally Released Under LGPL - original licence link has changed is not relivant.
34580  *
34581  * Fork - LGPL
34582  * <script type="text/javascript">
34583  */
34584
34585 /**
34586  * @class Roo.bootstrap.layout.Manager
34587  * @extends Roo.bootstrap.Component
34588  * Base class for layout managers.
34589  */
34590 Roo.bootstrap.layout.Manager = function(config)
34591 {
34592     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34593
34594
34595
34596
34597
34598     /** false to disable window resize monitoring @type Boolean */
34599     this.monitorWindowResize = true;
34600     this.regions = {};
34601     this.addEvents({
34602         /**
34603          * @event layout
34604          * Fires when a layout is performed.
34605          * @param {Roo.LayoutManager} this
34606          */
34607         "layout" : true,
34608         /**
34609          * @event regionresized
34610          * Fires when the user resizes a region.
34611          * @param {Roo.LayoutRegion} region The resized region
34612          * @param {Number} newSize The new size (width for east/west, height for north/south)
34613          */
34614         "regionresized" : true,
34615         /**
34616          * @event regioncollapsed
34617          * Fires when a region is collapsed.
34618          * @param {Roo.LayoutRegion} region The collapsed region
34619          */
34620         "regioncollapsed" : true,
34621         /**
34622          * @event regionexpanded
34623          * Fires when a region is expanded.
34624          * @param {Roo.LayoutRegion} region The expanded region
34625          */
34626         "regionexpanded" : true
34627     });
34628     this.updating = false;
34629
34630     if (config.el) {
34631         this.el = Roo.get(config.el);
34632         this.initEvents();
34633     }
34634
34635 };
34636
34637 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34638
34639
34640     regions : null,
34641
34642     monitorWindowResize : true,
34643
34644
34645     updating : false,
34646
34647
34648     onRender : function(ct, position)
34649     {
34650         if(!this.el){
34651             this.el = Roo.get(ct);
34652             this.initEvents();
34653         }
34654         //this.fireEvent('render',this);
34655     },
34656
34657
34658     initEvents: function()
34659     {
34660
34661
34662         // ie scrollbar fix
34663         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34664             document.body.scroll = "no";
34665         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34666             this.el.position('relative');
34667         }
34668         this.id = this.el.id;
34669         this.el.addClass("roo-layout-container");
34670         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34671         if(this.el.dom != document.body ) {
34672             this.el.on('resize', this.layout,this);
34673             this.el.on('show', this.layout,this);
34674         }
34675
34676     },
34677
34678     /**
34679      * Returns true if this layout is currently being updated
34680      * @return {Boolean}
34681      */
34682     isUpdating : function(){
34683         return this.updating;
34684     },
34685
34686     /**
34687      * Suspend the LayoutManager from doing auto-layouts while
34688      * making multiple add or remove calls
34689      */
34690     beginUpdate : function(){
34691         this.updating = true;
34692     },
34693
34694     /**
34695      * Restore auto-layouts and optionally disable the manager from performing a layout
34696      * @param {Boolean} noLayout true to disable a layout update
34697      */
34698     endUpdate : function(noLayout){
34699         this.updating = false;
34700         if(!noLayout){
34701             this.layout();
34702         }
34703     },
34704
34705     layout: function(){
34706         // abstract...
34707     },
34708
34709     onRegionResized : function(region, newSize){
34710         this.fireEvent("regionresized", region, newSize);
34711         this.layout();
34712     },
34713
34714     onRegionCollapsed : function(region){
34715         this.fireEvent("regioncollapsed", region);
34716     },
34717
34718     onRegionExpanded : function(region){
34719         this.fireEvent("regionexpanded", region);
34720     },
34721
34722     /**
34723      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34724      * performs box-model adjustments.
34725      * @return {Object} The size as an object {width: (the width), height: (the height)}
34726      */
34727     getViewSize : function()
34728     {
34729         var size;
34730         if(this.el.dom != document.body){
34731             size = this.el.getSize();
34732         }else{
34733             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34734         }
34735         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34736         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34737         return size;
34738     },
34739
34740     /**
34741      * Returns the Element this layout is bound to.
34742      * @return {Roo.Element}
34743      */
34744     getEl : function(){
34745         return this.el;
34746     },
34747
34748     /**
34749      * Returns the specified region.
34750      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34751      * @return {Roo.LayoutRegion}
34752      */
34753     getRegion : function(target){
34754         return this.regions[target.toLowerCase()];
34755     },
34756
34757     onWindowResize : function(){
34758         if(this.monitorWindowResize){
34759             this.layout();
34760         }
34761     }
34762 });
34763 /*
34764  * Based on:
34765  * Ext JS Library 1.1.1
34766  * Copyright(c) 2006-2007, Ext JS, LLC.
34767  *
34768  * Originally Released Under LGPL - original licence link has changed is not relivant.
34769  *
34770  * Fork - LGPL
34771  * <script type="text/javascript">
34772  */
34773 /**
34774  * @class Roo.bootstrap.layout.Border
34775  * @extends Roo.bootstrap.layout.Manager
34776  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34777  * please see: examples/bootstrap/nested.html<br><br>
34778  
34779 <b>The container the layout is rendered into can be either the body element or any other element.
34780 If it is not the body element, the container needs to either be an absolute positioned element,
34781 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34782 the container size if it is not the body element.</b>
34783
34784 * @constructor
34785 * Create a new Border
34786 * @param {Object} config Configuration options
34787  */
34788 Roo.bootstrap.layout.Border = function(config){
34789     config = config || {};
34790     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34791     
34792     
34793     
34794     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34795         if(config[region]){
34796             config[region].region = region;
34797             this.addRegion(config[region]);
34798         }
34799     },this);
34800     
34801 };
34802
34803 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34804
34805 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34806     /**
34807      * Creates and adds a new region if it doesn't already exist.
34808      * @param {String} target The target region key (north, south, east, west or center).
34809      * @param {Object} config The regions config object
34810      * @return {BorderLayoutRegion} The new region
34811      */
34812     addRegion : function(config)
34813     {
34814         if(!this.regions[config.region]){
34815             var r = this.factory(config);
34816             this.bindRegion(r);
34817         }
34818         return this.regions[config.region];
34819     },
34820
34821     // private (kinda)
34822     bindRegion : function(r){
34823         this.regions[r.config.region] = r;
34824         
34825         r.on("visibilitychange",    this.layout, this);
34826         r.on("paneladded",          this.layout, this);
34827         r.on("panelremoved",        this.layout, this);
34828         r.on("invalidated",         this.layout, this);
34829         r.on("resized",             this.onRegionResized, this);
34830         r.on("collapsed",           this.onRegionCollapsed, this);
34831         r.on("expanded",            this.onRegionExpanded, this);
34832     },
34833
34834     /**
34835      * Performs a layout update.
34836      */
34837     layout : function()
34838     {
34839         if(this.updating) {
34840             return;
34841         }
34842         
34843         // render all the rebions if they have not been done alreayd?
34844         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34845             if(this.regions[region] && !this.regions[region].bodyEl){
34846                 this.regions[region].onRender(this.el)
34847             }
34848         },this);
34849         
34850         var size = this.getViewSize();
34851         var w = size.width;
34852         var h = size.height;
34853         var centerW = w;
34854         var centerH = h;
34855         var centerY = 0;
34856         var centerX = 0;
34857         //var x = 0, y = 0;
34858
34859         var rs = this.regions;
34860         var north = rs["north"];
34861         var south = rs["south"]; 
34862         var west = rs["west"];
34863         var east = rs["east"];
34864         var center = rs["center"];
34865         //if(this.hideOnLayout){ // not supported anymore
34866             //c.el.setStyle("display", "none");
34867         //}
34868         if(north && north.isVisible()){
34869             var b = north.getBox();
34870             var m = north.getMargins();
34871             b.width = w - (m.left+m.right);
34872             b.x = m.left;
34873             b.y = m.top;
34874             centerY = b.height + b.y + m.bottom;
34875             centerH -= centerY;
34876             north.updateBox(this.safeBox(b));
34877         }
34878         if(south && south.isVisible()){
34879             var b = south.getBox();
34880             var m = south.getMargins();
34881             b.width = w - (m.left+m.right);
34882             b.x = m.left;
34883             var totalHeight = (b.height + m.top + m.bottom);
34884             b.y = h - totalHeight + m.top;
34885             centerH -= totalHeight;
34886             south.updateBox(this.safeBox(b));
34887         }
34888         if(west && west.isVisible()){
34889             var b = west.getBox();
34890             var m = west.getMargins();
34891             b.height = centerH - (m.top+m.bottom);
34892             b.x = m.left;
34893             b.y = centerY + m.top;
34894             var totalWidth = (b.width + m.left + m.right);
34895             centerX += totalWidth;
34896             centerW -= totalWidth;
34897             west.updateBox(this.safeBox(b));
34898         }
34899         if(east && east.isVisible()){
34900             var b = east.getBox();
34901             var m = east.getMargins();
34902             b.height = centerH - (m.top+m.bottom);
34903             var totalWidth = (b.width + m.left + m.right);
34904             b.x = w - totalWidth + m.left;
34905             b.y = centerY + m.top;
34906             centerW -= totalWidth;
34907             east.updateBox(this.safeBox(b));
34908         }
34909         if(center){
34910             var m = center.getMargins();
34911             var centerBox = {
34912                 x: centerX + m.left,
34913                 y: centerY + m.top,
34914                 width: centerW - (m.left+m.right),
34915                 height: centerH - (m.top+m.bottom)
34916             };
34917             //if(this.hideOnLayout){
34918                 //center.el.setStyle("display", "block");
34919             //}
34920             center.updateBox(this.safeBox(centerBox));
34921         }
34922         this.el.repaint();
34923         this.fireEvent("layout", this);
34924     },
34925
34926     // private
34927     safeBox : function(box){
34928         box.width = Math.max(0, box.width);
34929         box.height = Math.max(0, box.height);
34930         return box;
34931     },
34932
34933     /**
34934      * Adds a ContentPanel (or subclass) to this layout.
34935      * @param {String} target The target region key (north, south, east, west or center).
34936      * @param {Roo.ContentPanel} panel The panel to add
34937      * @return {Roo.ContentPanel} The added panel
34938      */
34939     add : function(target, panel){
34940          
34941         target = target.toLowerCase();
34942         return this.regions[target].add(panel);
34943     },
34944
34945     /**
34946      * Remove a ContentPanel (or subclass) to this layout.
34947      * @param {String} target The target region key (north, south, east, west or center).
34948      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34949      * @return {Roo.ContentPanel} The removed panel
34950      */
34951     remove : function(target, panel){
34952         target = target.toLowerCase();
34953         return this.regions[target].remove(panel);
34954     },
34955
34956     /**
34957      * Searches all regions for a panel with the specified id
34958      * @param {String} panelId
34959      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34960      */
34961     findPanel : function(panelId){
34962         var rs = this.regions;
34963         for(var target in rs){
34964             if(typeof rs[target] != "function"){
34965                 var p = rs[target].getPanel(panelId);
34966                 if(p){
34967                     return p;
34968                 }
34969             }
34970         }
34971         return null;
34972     },
34973
34974     /**
34975      * Searches all regions for a panel with the specified id and activates (shows) it.
34976      * @param {String/ContentPanel} panelId The panels id or the panel itself
34977      * @return {Roo.ContentPanel} The shown panel or null
34978      */
34979     showPanel : function(panelId) {
34980       var rs = this.regions;
34981       for(var target in rs){
34982          var r = rs[target];
34983          if(typeof r != "function"){
34984             if(r.hasPanel(panelId)){
34985                return r.showPanel(panelId);
34986             }
34987          }
34988       }
34989       return null;
34990    },
34991
34992    /**
34993      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34994      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34995      */
34996    /*
34997     restoreState : function(provider){
34998         if(!provider){
34999             provider = Roo.state.Manager;
35000         }
35001         var sm = new Roo.LayoutStateManager();
35002         sm.init(this, provider);
35003     },
35004 */
35005  
35006  
35007     /**
35008      * Adds a xtype elements to the layout.
35009      * <pre><code>
35010
35011 layout.addxtype({
35012        xtype : 'ContentPanel',
35013        region: 'west',
35014        items: [ .... ]
35015    }
35016 );
35017
35018 layout.addxtype({
35019         xtype : 'NestedLayoutPanel',
35020         region: 'west',
35021         layout: {
35022            center: { },
35023            west: { }   
35024         },
35025         items : [ ... list of content panels or nested layout panels.. ]
35026    }
35027 );
35028 </code></pre>
35029      * @param {Object} cfg Xtype definition of item to add.
35030      */
35031     addxtype : function(cfg)
35032     {
35033         // basically accepts a pannel...
35034         // can accept a layout region..!?!?
35035         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35036         
35037         
35038         // theory?  children can only be panels??
35039         
35040         //if (!cfg.xtype.match(/Panel$/)) {
35041         //    return false;
35042         //}
35043         var ret = false;
35044         
35045         if (typeof(cfg.region) == 'undefined') {
35046             Roo.log("Failed to add Panel, region was not set");
35047             Roo.log(cfg);
35048             return false;
35049         }
35050         var region = cfg.region;
35051         delete cfg.region;
35052         
35053           
35054         var xitems = [];
35055         if (cfg.items) {
35056             xitems = cfg.items;
35057             delete cfg.items;
35058         }
35059         var nb = false;
35060         
35061         switch(cfg.xtype) 
35062         {
35063             case 'Content':  // ContentPanel (el, cfg)
35064             case 'Scroll':  // ContentPanel (el, cfg)
35065             case 'View': 
35066                 cfg.autoCreate = true;
35067                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35068                 //} else {
35069                 //    var el = this.el.createChild();
35070                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35071                 //}
35072                 
35073                 this.add(region, ret);
35074                 break;
35075             
35076             /*
35077             case 'TreePanel': // our new panel!
35078                 cfg.el = this.el.createChild();
35079                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35080                 this.add(region, ret);
35081                 break;
35082             */
35083             
35084             case 'Nest': 
35085                 // create a new Layout (which is  a Border Layout...
35086                 
35087                 var clayout = cfg.layout;
35088                 clayout.el  = this.el.createChild();
35089                 clayout.items   = clayout.items  || [];
35090                 
35091                 delete cfg.layout;
35092                 
35093                 // replace this exitems with the clayout ones..
35094                 xitems = clayout.items;
35095                  
35096                 // force background off if it's in center...
35097                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35098                     cfg.background = false;
35099                 }
35100                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35101                 
35102                 
35103                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35104                 //console.log('adding nested layout panel '  + cfg.toSource());
35105                 this.add(region, ret);
35106                 nb = {}; /// find first...
35107                 break;
35108             
35109             case 'Grid':
35110                 
35111                 // needs grid and region
35112                 
35113                 //var el = this.getRegion(region).el.createChild();
35114                 /*
35115                  *var el = this.el.createChild();
35116                 // create the grid first...
35117                 cfg.grid.container = el;
35118                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35119                 */
35120                 
35121                 if (region == 'center' && this.active ) {
35122                     cfg.background = false;
35123                 }
35124                 
35125                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35126                 
35127                 this.add(region, ret);
35128                 /*
35129                 if (cfg.background) {
35130                     // render grid on panel activation (if panel background)
35131                     ret.on('activate', function(gp) {
35132                         if (!gp.grid.rendered) {
35133                     //        gp.grid.render(el);
35134                         }
35135                     });
35136                 } else {
35137                   //  cfg.grid.render(el);
35138                 }
35139                 */
35140                 break;
35141            
35142            
35143             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35144                 // it was the old xcomponent building that caused this before.
35145                 // espeically if border is the top element in the tree.
35146                 ret = this;
35147                 break; 
35148                 
35149                     
35150                 
35151                 
35152                 
35153             default:
35154                 /*
35155                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35156                     
35157                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35158                     this.add(region, ret);
35159                 } else {
35160                 */
35161                     Roo.log(cfg);
35162                     throw "Can not add '" + cfg.xtype + "' to Border";
35163                     return null;
35164              
35165                                 
35166              
35167         }
35168         this.beginUpdate();
35169         // add children..
35170         var region = '';
35171         var abn = {};
35172         Roo.each(xitems, function(i)  {
35173             region = nb && i.region ? i.region : false;
35174             
35175             var add = ret.addxtype(i);
35176            
35177             if (region) {
35178                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35179                 if (!i.background) {
35180                     abn[region] = nb[region] ;
35181                 }
35182             }
35183             
35184         });
35185         this.endUpdate();
35186
35187         // make the last non-background panel active..
35188         //if (nb) { Roo.log(abn); }
35189         if (nb) {
35190             
35191             for(var r in abn) {
35192                 region = this.getRegion(r);
35193                 if (region) {
35194                     // tried using nb[r], but it does not work..
35195                      
35196                     region.showPanel(abn[r]);
35197                    
35198                 }
35199             }
35200         }
35201         return ret;
35202         
35203     },
35204     
35205     
35206 // private
35207     factory : function(cfg)
35208     {
35209         
35210         var validRegions = Roo.bootstrap.layout.Border.regions;
35211
35212         var target = cfg.region;
35213         cfg.mgr = this;
35214         
35215         var r = Roo.bootstrap.layout;
35216         Roo.log(target);
35217         switch(target){
35218             case "north":
35219                 return new r.North(cfg);
35220             case "south":
35221                 return new r.South(cfg);
35222             case "east":
35223                 return new r.East(cfg);
35224             case "west":
35225                 return new r.West(cfg);
35226             case "center":
35227                 return new r.Center(cfg);
35228         }
35229         throw 'Layout region "'+target+'" not supported.';
35230     }
35231     
35232     
35233 });
35234  /*
35235  * Based on:
35236  * Ext JS Library 1.1.1
35237  * Copyright(c) 2006-2007, Ext JS, LLC.
35238  *
35239  * Originally Released Under LGPL - original licence link has changed is not relivant.
35240  *
35241  * Fork - LGPL
35242  * <script type="text/javascript">
35243  */
35244  
35245 /**
35246  * @class Roo.bootstrap.layout.Basic
35247  * @extends Roo.util.Observable
35248  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35249  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35250  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35251  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35252  * @cfg {string}   region  the region that it inhabits..
35253  * @cfg {bool}   skipConfig skip config?
35254  * 
35255
35256  */
35257 Roo.bootstrap.layout.Basic = function(config){
35258     
35259     this.mgr = config.mgr;
35260     
35261     this.position = config.region;
35262     
35263     var skipConfig = config.skipConfig;
35264     
35265     this.events = {
35266         /**
35267          * @scope Roo.BasicLayoutRegion
35268          */
35269         
35270         /**
35271          * @event beforeremove
35272          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35273          * @param {Roo.LayoutRegion} this
35274          * @param {Roo.ContentPanel} panel The panel
35275          * @param {Object} e The cancel event object
35276          */
35277         "beforeremove" : true,
35278         /**
35279          * @event invalidated
35280          * Fires when the layout for this region is changed.
35281          * @param {Roo.LayoutRegion} this
35282          */
35283         "invalidated" : true,
35284         /**
35285          * @event visibilitychange
35286          * Fires when this region is shown or hidden 
35287          * @param {Roo.LayoutRegion} this
35288          * @param {Boolean} visibility true or false
35289          */
35290         "visibilitychange" : true,
35291         /**
35292          * @event paneladded
35293          * Fires when a panel is added. 
35294          * @param {Roo.LayoutRegion} this
35295          * @param {Roo.ContentPanel} panel The panel
35296          */
35297         "paneladded" : true,
35298         /**
35299          * @event panelremoved
35300          * Fires when a panel is removed. 
35301          * @param {Roo.LayoutRegion} this
35302          * @param {Roo.ContentPanel} panel The panel
35303          */
35304         "panelremoved" : true,
35305         /**
35306          * @event beforecollapse
35307          * Fires when this region before collapse.
35308          * @param {Roo.LayoutRegion} this
35309          */
35310         "beforecollapse" : true,
35311         /**
35312          * @event collapsed
35313          * Fires when this region is collapsed.
35314          * @param {Roo.LayoutRegion} this
35315          */
35316         "collapsed" : true,
35317         /**
35318          * @event expanded
35319          * Fires when this region is expanded.
35320          * @param {Roo.LayoutRegion} this
35321          */
35322         "expanded" : true,
35323         /**
35324          * @event slideshow
35325          * Fires when this region is slid into view.
35326          * @param {Roo.LayoutRegion} this
35327          */
35328         "slideshow" : true,
35329         /**
35330          * @event slidehide
35331          * Fires when this region slides out of view. 
35332          * @param {Roo.LayoutRegion} this
35333          */
35334         "slidehide" : true,
35335         /**
35336          * @event panelactivated
35337          * Fires when a panel is activated. 
35338          * @param {Roo.LayoutRegion} this
35339          * @param {Roo.ContentPanel} panel The activated panel
35340          */
35341         "panelactivated" : true,
35342         /**
35343          * @event resized
35344          * Fires when the user resizes this region. 
35345          * @param {Roo.LayoutRegion} this
35346          * @param {Number} newSize The new size (width for east/west, height for north/south)
35347          */
35348         "resized" : true
35349     };
35350     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35351     this.panels = new Roo.util.MixedCollection();
35352     this.panels.getKey = this.getPanelId.createDelegate(this);
35353     this.box = null;
35354     this.activePanel = null;
35355     // ensure listeners are added...
35356     
35357     if (config.listeners || config.events) {
35358         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35359             listeners : config.listeners || {},
35360             events : config.events || {}
35361         });
35362     }
35363     
35364     if(skipConfig !== true){
35365         this.applyConfig(config);
35366     }
35367 };
35368
35369 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35370 {
35371     getPanelId : function(p){
35372         return p.getId();
35373     },
35374     
35375     applyConfig : function(config){
35376         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35377         this.config = config;
35378         
35379     },
35380     
35381     /**
35382      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35383      * the width, for horizontal (north, south) the height.
35384      * @param {Number} newSize The new width or height
35385      */
35386     resizeTo : function(newSize){
35387         var el = this.el ? this.el :
35388                  (this.activePanel ? this.activePanel.getEl() : null);
35389         if(el){
35390             switch(this.position){
35391                 case "east":
35392                 case "west":
35393                     el.setWidth(newSize);
35394                     this.fireEvent("resized", this, newSize);
35395                 break;
35396                 case "north":
35397                 case "south":
35398                     el.setHeight(newSize);
35399                     this.fireEvent("resized", this, newSize);
35400                 break;                
35401             }
35402         }
35403     },
35404     
35405     getBox : function(){
35406         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35407     },
35408     
35409     getMargins : function(){
35410         return this.margins;
35411     },
35412     
35413     updateBox : function(box){
35414         this.box = box;
35415         var el = this.activePanel.getEl();
35416         el.dom.style.left = box.x + "px";
35417         el.dom.style.top = box.y + "px";
35418         this.activePanel.setSize(box.width, box.height);
35419     },
35420     
35421     /**
35422      * Returns the container element for this region.
35423      * @return {Roo.Element}
35424      */
35425     getEl : function(){
35426         return this.activePanel;
35427     },
35428     
35429     /**
35430      * Returns true if this region is currently visible.
35431      * @return {Boolean}
35432      */
35433     isVisible : function(){
35434         return this.activePanel ? true : false;
35435     },
35436     
35437     setActivePanel : function(panel){
35438         panel = this.getPanel(panel);
35439         if(this.activePanel && this.activePanel != panel){
35440             this.activePanel.setActiveState(false);
35441             this.activePanel.getEl().setLeftTop(-10000,-10000);
35442         }
35443         this.activePanel = panel;
35444         panel.setActiveState(true);
35445         if(this.box){
35446             panel.setSize(this.box.width, this.box.height);
35447         }
35448         this.fireEvent("panelactivated", this, panel);
35449         this.fireEvent("invalidated");
35450     },
35451     
35452     /**
35453      * Show the specified panel.
35454      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35455      * @return {Roo.ContentPanel} The shown panel or null
35456      */
35457     showPanel : function(panel){
35458         panel = this.getPanel(panel);
35459         if(panel){
35460             this.setActivePanel(panel);
35461         }
35462         return panel;
35463     },
35464     
35465     /**
35466      * Get the active panel for this region.
35467      * @return {Roo.ContentPanel} The active panel or null
35468      */
35469     getActivePanel : function(){
35470         return this.activePanel;
35471     },
35472     
35473     /**
35474      * Add the passed ContentPanel(s)
35475      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35476      * @return {Roo.ContentPanel} The panel added (if only one was added)
35477      */
35478     add : function(panel){
35479         if(arguments.length > 1){
35480             for(var i = 0, len = arguments.length; i < len; i++) {
35481                 this.add(arguments[i]);
35482             }
35483             return null;
35484         }
35485         if(this.hasPanel(panel)){
35486             this.showPanel(panel);
35487             return panel;
35488         }
35489         var el = panel.getEl();
35490         if(el.dom.parentNode != this.mgr.el.dom){
35491             this.mgr.el.dom.appendChild(el.dom);
35492         }
35493         if(panel.setRegion){
35494             panel.setRegion(this);
35495         }
35496         this.panels.add(panel);
35497         el.setStyle("position", "absolute");
35498         if(!panel.background){
35499             this.setActivePanel(panel);
35500             if(this.config.initialSize && this.panels.getCount()==1){
35501                 this.resizeTo(this.config.initialSize);
35502             }
35503         }
35504         this.fireEvent("paneladded", this, panel);
35505         return panel;
35506     },
35507     
35508     /**
35509      * Returns true if the panel is in this region.
35510      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35511      * @return {Boolean}
35512      */
35513     hasPanel : function(panel){
35514         if(typeof panel == "object"){ // must be panel obj
35515             panel = panel.getId();
35516         }
35517         return this.getPanel(panel) ? true : false;
35518     },
35519     
35520     /**
35521      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35522      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35523      * @param {Boolean} preservePanel Overrides the config preservePanel option
35524      * @return {Roo.ContentPanel} The panel that was removed
35525      */
35526     remove : function(panel, preservePanel){
35527         panel = this.getPanel(panel);
35528         if(!panel){
35529             return null;
35530         }
35531         var e = {};
35532         this.fireEvent("beforeremove", this, panel, e);
35533         if(e.cancel === true){
35534             return null;
35535         }
35536         var panelId = panel.getId();
35537         this.panels.removeKey(panelId);
35538         return panel;
35539     },
35540     
35541     /**
35542      * Returns the panel specified or null if it's not in this region.
35543      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35544      * @return {Roo.ContentPanel}
35545      */
35546     getPanel : function(id){
35547         if(typeof id == "object"){ // must be panel obj
35548             return id;
35549         }
35550         return this.panels.get(id);
35551     },
35552     
35553     /**
35554      * Returns this regions position (north/south/east/west/center).
35555      * @return {String} 
35556      */
35557     getPosition: function(){
35558         return this.position;    
35559     }
35560 });/*
35561  * Based on:
35562  * Ext JS Library 1.1.1
35563  * Copyright(c) 2006-2007, Ext JS, LLC.
35564  *
35565  * Originally Released Under LGPL - original licence link has changed is not relivant.
35566  *
35567  * Fork - LGPL
35568  * <script type="text/javascript">
35569  */
35570  
35571 /**
35572  * @class Roo.bootstrap.layout.Region
35573  * @extends Roo.bootstrap.layout.Basic
35574  * This class represents a region in a layout manager.
35575  
35576  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35577  * @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})
35578  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35579  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35580  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35581  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35582  * @cfg {String}    title           The title for the region (overrides panel titles)
35583  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35584  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35585  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35586  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35587  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35588  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35589  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35590  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35591  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35592  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35593
35594  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35595  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35596  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35597  * @cfg {Number}    width           For East/West panels
35598  * @cfg {Number}    height          For North/South panels
35599  * @cfg {Boolean}   split           To show the splitter
35600  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35601  * 
35602  * @cfg {string}   cls             Extra CSS classes to add to region
35603  * 
35604  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35605  * @cfg {string}   region  the region that it inhabits..
35606  *
35607
35608  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35609  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35610
35611  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35612  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35613  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35614  */
35615 Roo.bootstrap.layout.Region = function(config)
35616 {
35617     this.applyConfig(config);
35618
35619     var mgr = config.mgr;
35620     var pos = config.region;
35621     config.skipConfig = true;
35622     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35623     
35624     if (mgr.el) {
35625         this.onRender(mgr.el);   
35626     }
35627      
35628     this.visible = true;
35629     this.collapsed = false;
35630     this.unrendered_panels = [];
35631 };
35632
35633 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35634
35635     position: '', // set by wrapper (eg. north/south etc..)
35636     unrendered_panels : null,  // unrendered panels.
35637     createBody : function(){
35638         /** This region's body element 
35639         * @type Roo.Element */
35640         this.bodyEl = this.el.createChild({
35641                 tag: "div",
35642                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35643         });
35644     },
35645
35646     onRender: function(ctr, pos)
35647     {
35648         var dh = Roo.DomHelper;
35649         /** This region's container element 
35650         * @type Roo.Element */
35651         this.el = dh.append(ctr.dom, {
35652                 tag: "div",
35653                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35654             }, true);
35655         /** This region's title element 
35656         * @type Roo.Element */
35657     
35658         this.titleEl = dh.append(this.el.dom,
35659             {
35660                     tag: "div",
35661                     unselectable: "on",
35662                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35663                     children:[
35664                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35665                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35666                     ]}, true);
35667         
35668         this.titleEl.enableDisplayMode();
35669         /** This region's title text element 
35670         * @type HTMLElement */
35671         this.titleTextEl = this.titleEl.dom.firstChild;
35672         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35673         /*
35674         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35675         this.closeBtn.enableDisplayMode();
35676         this.closeBtn.on("click", this.closeClicked, this);
35677         this.closeBtn.hide();
35678     */
35679         this.createBody(this.config);
35680         if(this.config.hideWhenEmpty){
35681             this.hide();
35682             this.on("paneladded", this.validateVisibility, this);
35683             this.on("panelremoved", this.validateVisibility, this);
35684         }
35685         if(this.autoScroll){
35686             this.bodyEl.setStyle("overflow", "auto");
35687         }else{
35688             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35689         }
35690         //if(c.titlebar !== false){
35691             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35692                 this.titleEl.hide();
35693             }else{
35694                 this.titleEl.show();
35695                 if(this.config.title){
35696                     this.titleTextEl.innerHTML = this.config.title;
35697                 }
35698             }
35699         //}
35700         if(this.config.collapsed){
35701             this.collapse(true);
35702         }
35703         if(this.config.hidden){
35704             this.hide();
35705         }
35706         
35707         if (this.unrendered_panels && this.unrendered_panels.length) {
35708             for (var i =0;i< this.unrendered_panels.length; i++) {
35709                 this.add(this.unrendered_panels[i]);
35710             }
35711             this.unrendered_panels = null;
35712             
35713         }
35714         
35715     },
35716     
35717     applyConfig : function(c)
35718     {
35719         /*
35720          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35721             var dh = Roo.DomHelper;
35722             if(c.titlebar !== false){
35723                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35724                 this.collapseBtn.on("click", this.collapse, this);
35725                 this.collapseBtn.enableDisplayMode();
35726                 /*
35727                 if(c.showPin === true || this.showPin){
35728                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35729                     this.stickBtn.enableDisplayMode();
35730                     this.stickBtn.on("click", this.expand, this);
35731                     this.stickBtn.hide();
35732                 }
35733                 
35734             }
35735             */
35736             /** This region's collapsed element
35737             * @type Roo.Element */
35738             /*
35739              *
35740             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35741                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35742             ]}, true);
35743             
35744             if(c.floatable !== false){
35745                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35746                this.collapsedEl.on("click", this.collapseClick, this);
35747             }
35748
35749             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35750                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35751                    id: "message", unselectable: "on", style:{"float":"left"}});
35752                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35753              }
35754             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35755             this.expandBtn.on("click", this.expand, this);
35756             
35757         }
35758         
35759         if(this.collapseBtn){
35760             this.collapseBtn.setVisible(c.collapsible == true);
35761         }
35762         
35763         this.cmargins = c.cmargins || this.cmargins ||
35764                          (this.position == "west" || this.position == "east" ?
35765                              {top: 0, left: 2, right:2, bottom: 0} :
35766                              {top: 2, left: 0, right:0, bottom: 2});
35767         */
35768         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35769         
35770         
35771         this.bottomTabs = c.tabPosition != "top";
35772         
35773         this.autoScroll = c.autoScroll || false;
35774         
35775         
35776        
35777         
35778         this.duration = c.duration || .30;
35779         this.slideDuration = c.slideDuration || .45;
35780         this.config = c;
35781        
35782     },
35783     /**
35784      * Returns true if this region is currently visible.
35785      * @return {Boolean}
35786      */
35787     isVisible : function(){
35788         return this.visible;
35789     },
35790
35791     /**
35792      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35793      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35794      */
35795     //setCollapsedTitle : function(title){
35796     //    title = title || "&#160;";
35797      //   if(this.collapsedTitleTextEl){
35798       //      this.collapsedTitleTextEl.innerHTML = title;
35799        // }
35800     //},
35801
35802     getBox : function(){
35803         var b;
35804       //  if(!this.collapsed){
35805             b = this.el.getBox(false, true);
35806        // }else{
35807           //  b = this.collapsedEl.getBox(false, true);
35808         //}
35809         return b;
35810     },
35811
35812     getMargins : function(){
35813         return this.margins;
35814         //return this.collapsed ? this.cmargins : this.margins;
35815     },
35816 /*
35817     highlight : function(){
35818         this.el.addClass("x-layout-panel-dragover");
35819     },
35820
35821     unhighlight : function(){
35822         this.el.removeClass("x-layout-panel-dragover");
35823     },
35824 */
35825     updateBox : function(box)
35826     {
35827         if (!this.bodyEl) {
35828             return; // not rendered yet..
35829         }
35830         
35831         this.box = box;
35832         if(!this.collapsed){
35833             this.el.dom.style.left = box.x + "px";
35834             this.el.dom.style.top = box.y + "px";
35835             this.updateBody(box.width, box.height);
35836         }else{
35837             this.collapsedEl.dom.style.left = box.x + "px";
35838             this.collapsedEl.dom.style.top = box.y + "px";
35839             this.collapsedEl.setSize(box.width, box.height);
35840         }
35841         if(this.tabs){
35842             this.tabs.autoSizeTabs();
35843         }
35844     },
35845
35846     updateBody : function(w, h)
35847     {
35848         if(w !== null){
35849             this.el.setWidth(w);
35850             w -= this.el.getBorderWidth("rl");
35851             if(this.config.adjustments){
35852                 w += this.config.adjustments[0];
35853             }
35854         }
35855         if(h !== null && h > 0){
35856             this.el.setHeight(h);
35857             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35858             h -= this.el.getBorderWidth("tb");
35859             if(this.config.adjustments){
35860                 h += this.config.adjustments[1];
35861             }
35862             this.bodyEl.setHeight(h);
35863             if(this.tabs){
35864                 h = this.tabs.syncHeight(h);
35865             }
35866         }
35867         if(this.panelSize){
35868             w = w !== null ? w : this.panelSize.width;
35869             h = h !== null ? h : this.panelSize.height;
35870         }
35871         if(this.activePanel){
35872             var el = this.activePanel.getEl();
35873             w = w !== null ? w : el.getWidth();
35874             h = h !== null ? h : el.getHeight();
35875             this.panelSize = {width: w, height: h};
35876             this.activePanel.setSize(w, h);
35877         }
35878         if(Roo.isIE && this.tabs){
35879             this.tabs.el.repaint();
35880         }
35881     },
35882
35883     /**
35884      * Returns the container element for this region.
35885      * @return {Roo.Element}
35886      */
35887     getEl : function(){
35888         return this.el;
35889     },
35890
35891     /**
35892      * Hides this region.
35893      */
35894     hide : function(){
35895         //if(!this.collapsed){
35896             this.el.dom.style.left = "-2000px";
35897             this.el.hide();
35898         //}else{
35899          //   this.collapsedEl.dom.style.left = "-2000px";
35900          //   this.collapsedEl.hide();
35901        // }
35902         this.visible = false;
35903         this.fireEvent("visibilitychange", this, false);
35904     },
35905
35906     /**
35907      * Shows this region if it was previously hidden.
35908      */
35909     show : function(){
35910         //if(!this.collapsed){
35911             this.el.show();
35912         //}else{
35913         //    this.collapsedEl.show();
35914        // }
35915         this.visible = true;
35916         this.fireEvent("visibilitychange", this, true);
35917     },
35918 /*
35919     closeClicked : function(){
35920         if(this.activePanel){
35921             this.remove(this.activePanel);
35922         }
35923     },
35924
35925     collapseClick : function(e){
35926         if(this.isSlid){
35927            e.stopPropagation();
35928            this.slideIn();
35929         }else{
35930            e.stopPropagation();
35931            this.slideOut();
35932         }
35933     },
35934 */
35935     /**
35936      * Collapses this region.
35937      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35938      */
35939     /*
35940     collapse : function(skipAnim, skipCheck = false){
35941         if(this.collapsed) {
35942             return;
35943         }
35944         
35945         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35946             
35947             this.collapsed = true;
35948             if(this.split){
35949                 this.split.el.hide();
35950             }
35951             if(this.config.animate && skipAnim !== true){
35952                 this.fireEvent("invalidated", this);
35953                 this.animateCollapse();
35954             }else{
35955                 this.el.setLocation(-20000,-20000);
35956                 this.el.hide();
35957                 this.collapsedEl.show();
35958                 this.fireEvent("collapsed", this);
35959                 this.fireEvent("invalidated", this);
35960             }
35961         }
35962         
35963     },
35964 */
35965     animateCollapse : function(){
35966         // overridden
35967     },
35968
35969     /**
35970      * Expands this region if it was previously collapsed.
35971      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35972      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35973      */
35974     /*
35975     expand : function(e, skipAnim){
35976         if(e) {
35977             e.stopPropagation();
35978         }
35979         if(!this.collapsed || this.el.hasActiveFx()) {
35980             return;
35981         }
35982         if(this.isSlid){
35983             this.afterSlideIn();
35984             skipAnim = true;
35985         }
35986         this.collapsed = false;
35987         if(this.config.animate && skipAnim !== true){
35988             this.animateExpand();
35989         }else{
35990             this.el.show();
35991             if(this.split){
35992                 this.split.el.show();
35993             }
35994             this.collapsedEl.setLocation(-2000,-2000);
35995             this.collapsedEl.hide();
35996             this.fireEvent("invalidated", this);
35997             this.fireEvent("expanded", this);
35998         }
35999     },
36000 */
36001     animateExpand : function(){
36002         // overridden
36003     },
36004
36005     initTabs : function()
36006     {
36007         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36008         
36009         var ts = new Roo.bootstrap.panel.Tabs({
36010                 el: this.bodyEl.dom,
36011                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36012                 disableTooltips: this.config.disableTabTips,
36013                 toolbar : this.config.toolbar
36014             });
36015         
36016         if(this.config.hideTabs){
36017             ts.stripWrap.setDisplayed(false);
36018         }
36019         this.tabs = ts;
36020         ts.resizeTabs = this.config.resizeTabs === true;
36021         ts.minTabWidth = this.config.minTabWidth || 40;
36022         ts.maxTabWidth = this.config.maxTabWidth || 250;
36023         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36024         ts.monitorResize = false;
36025         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36026         ts.bodyEl.addClass('roo-layout-tabs-body');
36027         this.panels.each(this.initPanelAsTab, this);
36028     },
36029
36030     initPanelAsTab : function(panel){
36031         var ti = this.tabs.addTab(
36032             panel.getEl().id,
36033             panel.getTitle(),
36034             null,
36035             this.config.closeOnTab && panel.isClosable(),
36036             panel.tpl
36037         );
36038         if(panel.tabTip !== undefined){
36039             ti.setTooltip(panel.tabTip);
36040         }
36041         ti.on("activate", function(){
36042               this.setActivePanel(panel);
36043         }, this);
36044         
36045         if(this.config.closeOnTab){
36046             ti.on("beforeclose", function(t, e){
36047                 e.cancel = true;
36048                 this.remove(panel);
36049             }, this);
36050         }
36051         
36052         panel.tabItem = ti;
36053         
36054         return ti;
36055     },
36056
36057     updatePanelTitle : function(panel, title)
36058     {
36059         if(this.activePanel == panel){
36060             this.updateTitle(title);
36061         }
36062         if(this.tabs){
36063             var ti = this.tabs.getTab(panel.getEl().id);
36064             ti.setText(title);
36065             if(panel.tabTip !== undefined){
36066                 ti.setTooltip(panel.tabTip);
36067             }
36068         }
36069     },
36070
36071     updateTitle : function(title){
36072         if(this.titleTextEl && !this.config.title){
36073             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36074         }
36075     },
36076
36077     setActivePanel : function(panel)
36078     {
36079         panel = this.getPanel(panel);
36080         if(this.activePanel && this.activePanel != panel){
36081             if(this.activePanel.setActiveState(false) === false){
36082                 return;
36083             }
36084         }
36085         this.activePanel = panel;
36086         panel.setActiveState(true);
36087         if(this.panelSize){
36088             panel.setSize(this.panelSize.width, this.panelSize.height);
36089         }
36090         if(this.closeBtn){
36091             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36092         }
36093         this.updateTitle(panel.getTitle());
36094         if(this.tabs){
36095             this.fireEvent("invalidated", this);
36096         }
36097         this.fireEvent("panelactivated", this, panel);
36098     },
36099
36100     /**
36101      * Shows the specified panel.
36102      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36103      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36104      */
36105     showPanel : function(panel)
36106     {
36107         panel = this.getPanel(panel);
36108         if(panel){
36109             if(this.tabs){
36110                 var tab = this.tabs.getTab(panel.getEl().id);
36111                 if(tab.isHidden()){
36112                     this.tabs.unhideTab(tab.id);
36113                 }
36114                 tab.activate();
36115             }else{
36116                 this.setActivePanel(panel);
36117             }
36118         }
36119         return panel;
36120     },
36121
36122     /**
36123      * Get the active panel for this region.
36124      * @return {Roo.ContentPanel} The active panel or null
36125      */
36126     getActivePanel : function(){
36127         return this.activePanel;
36128     },
36129
36130     validateVisibility : function(){
36131         if(this.panels.getCount() < 1){
36132             this.updateTitle("&#160;");
36133             this.closeBtn.hide();
36134             this.hide();
36135         }else{
36136             if(!this.isVisible()){
36137                 this.show();
36138             }
36139         }
36140     },
36141
36142     /**
36143      * Adds the passed ContentPanel(s) to this region.
36144      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36145      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36146      */
36147     add : function(panel)
36148     {
36149         if(arguments.length > 1){
36150             for(var i = 0, len = arguments.length; i < len; i++) {
36151                 this.add(arguments[i]);
36152             }
36153             return null;
36154         }
36155         
36156         // if we have not been rendered yet, then we can not really do much of this..
36157         if (!this.bodyEl) {
36158             this.unrendered_panels.push(panel);
36159             return panel;
36160         }
36161         
36162         
36163         
36164         
36165         if(this.hasPanel(panel)){
36166             this.showPanel(panel);
36167             return panel;
36168         }
36169         panel.setRegion(this);
36170         this.panels.add(panel);
36171        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36172             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36173             // and hide them... ???
36174             this.bodyEl.dom.appendChild(panel.getEl().dom);
36175             if(panel.background !== true){
36176                 this.setActivePanel(panel);
36177             }
36178             this.fireEvent("paneladded", this, panel);
36179             return panel;
36180         }
36181         */
36182         if(!this.tabs){
36183             this.initTabs();
36184         }else{
36185             this.initPanelAsTab(panel);
36186         }
36187         
36188         
36189         if(panel.background !== true){
36190             this.tabs.activate(panel.getEl().id);
36191         }
36192         this.fireEvent("paneladded", this, panel);
36193         return panel;
36194     },
36195
36196     /**
36197      * Hides the tab for the specified panel.
36198      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36199      */
36200     hidePanel : function(panel){
36201         if(this.tabs && (panel = this.getPanel(panel))){
36202             this.tabs.hideTab(panel.getEl().id);
36203         }
36204     },
36205
36206     /**
36207      * Unhides the tab for a previously hidden panel.
36208      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36209      */
36210     unhidePanel : function(panel){
36211         if(this.tabs && (panel = this.getPanel(panel))){
36212             this.tabs.unhideTab(panel.getEl().id);
36213         }
36214     },
36215
36216     clearPanels : function(){
36217         while(this.panels.getCount() > 0){
36218              this.remove(this.panels.first());
36219         }
36220     },
36221
36222     /**
36223      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36224      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36225      * @param {Boolean} preservePanel Overrides the config preservePanel option
36226      * @return {Roo.ContentPanel} The panel that was removed
36227      */
36228     remove : function(panel, preservePanel)
36229     {
36230         panel = this.getPanel(panel);
36231         if(!panel){
36232             return null;
36233         }
36234         var e = {};
36235         this.fireEvent("beforeremove", this, panel, e);
36236         if(e.cancel === true){
36237             return null;
36238         }
36239         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36240         var panelId = panel.getId();
36241         this.panels.removeKey(panelId);
36242         if(preservePanel){
36243             document.body.appendChild(panel.getEl().dom);
36244         }
36245         if(this.tabs){
36246             this.tabs.removeTab(panel.getEl().id);
36247         }else if (!preservePanel){
36248             this.bodyEl.dom.removeChild(panel.getEl().dom);
36249         }
36250         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36251             var p = this.panels.first();
36252             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36253             tempEl.appendChild(p.getEl().dom);
36254             this.bodyEl.update("");
36255             this.bodyEl.dom.appendChild(p.getEl().dom);
36256             tempEl = null;
36257             this.updateTitle(p.getTitle());
36258             this.tabs = null;
36259             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36260             this.setActivePanel(p);
36261         }
36262         panel.setRegion(null);
36263         if(this.activePanel == panel){
36264             this.activePanel = null;
36265         }
36266         if(this.config.autoDestroy !== false && preservePanel !== true){
36267             try{panel.destroy();}catch(e){}
36268         }
36269         this.fireEvent("panelremoved", this, panel);
36270         return panel;
36271     },
36272
36273     /**
36274      * Returns the TabPanel component used by this region
36275      * @return {Roo.TabPanel}
36276      */
36277     getTabs : function(){
36278         return this.tabs;
36279     },
36280
36281     createTool : function(parentEl, className){
36282         var btn = Roo.DomHelper.append(parentEl, {
36283             tag: "div",
36284             cls: "x-layout-tools-button",
36285             children: [ {
36286                 tag: "div",
36287                 cls: "roo-layout-tools-button-inner " + className,
36288                 html: "&#160;"
36289             }]
36290         }, true);
36291         btn.addClassOnOver("roo-layout-tools-button-over");
36292         return btn;
36293     }
36294 });/*
36295  * Based on:
36296  * Ext JS Library 1.1.1
36297  * Copyright(c) 2006-2007, Ext JS, LLC.
36298  *
36299  * Originally Released Under LGPL - original licence link has changed is not relivant.
36300  *
36301  * Fork - LGPL
36302  * <script type="text/javascript">
36303  */
36304  
36305
36306
36307 /**
36308  * @class Roo.SplitLayoutRegion
36309  * @extends Roo.LayoutRegion
36310  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36311  */
36312 Roo.bootstrap.layout.Split = function(config){
36313     this.cursor = config.cursor;
36314     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36315 };
36316
36317 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36318 {
36319     splitTip : "Drag to resize.",
36320     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36321     useSplitTips : false,
36322
36323     applyConfig : function(config){
36324         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36325     },
36326     
36327     onRender : function(ctr,pos) {
36328         
36329         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36330         if(!this.config.split){
36331             return;
36332         }
36333         if(!this.split){
36334             
36335             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36336                             tag: "div",
36337                             id: this.el.id + "-split",
36338                             cls: "roo-layout-split roo-layout-split-"+this.position,
36339                             html: "&#160;"
36340             });
36341             /** The SplitBar for this region 
36342             * @type Roo.SplitBar */
36343             // does not exist yet...
36344             Roo.log([this.position, this.orientation]);
36345             
36346             this.split = new Roo.bootstrap.SplitBar({
36347                 dragElement : splitEl,
36348                 resizingElement: this.el,
36349                 orientation : this.orientation
36350             });
36351             
36352             this.split.on("moved", this.onSplitMove, this);
36353             this.split.useShim = this.config.useShim === true;
36354             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36355             if(this.useSplitTips){
36356                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36357             }
36358             //if(config.collapsible){
36359             //    this.split.el.on("dblclick", this.collapse,  this);
36360             //}
36361         }
36362         if(typeof this.config.minSize != "undefined"){
36363             this.split.minSize = this.config.minSize;
36364         }
36365         if(typeof this.config.maxSize != "undefined"){
36366             this.split.maxSize = this.config.maxSize;
36367         }
36368         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36369             this.hideSplitter();
36370         }
36371         
36372     },
36373
36374     getHMaxSize : function(){
36375          var cmax = this.config.maxSize || 10000;
36376          var center = this.mgr.getRegion("center");
36377          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36378     },
36379
36380     getVMaxSize : function(){
36381          var cmax = this.config.maxSize || 10000;
36382          var center = this.mgr.getRegion("center");
36383          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36384     },
36385
36386     onSplitMove : function(split, newSize){
36387         this.fireEvent("resized", this, newSize);
36388     },
36389     
36390     /** 
36391      * Returns the {@link Roo.SplitBar} for this region.
36392      * @return {Roo.SplitBar}
36393      */
36394     getSplitBar : function(){
36395         return this.split;
36396     },
36397     
36398     hide : function(){
36399         this.hideSplitter();
36400         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36401     },
36402
36403     hideSplitter : function(){
36404         if(this.split){
36405             this.split.el.setLocation(-2000,-2000);
36406             this.split.el.hide();
36407         }
36408     },
36409
36410     show : function(){
36411         if(this.split){
36412             this.split.el.show();
36413         }
36414         Roo.bootstrap.layout.Split.superclass.show.call(this);
36415     },
36416     
36417     beforeSlide: function(){
36418         if(Roo.isGecko){// firefox overflow auto bug workaround
36419             this.bodyEl.clip();
36420             if(this.tabs) {
36421                 this.tabs.bodyEl.clip();
36422             }
36423             if(this.activePanel){
36424                 this.activePanel.getEl().clip();
36425                 
36426                 if(this.activePanel.beforeSlide){
36427                     this.activePanel.beforeSlide();
36428                 }
36429             }
36430         }
36431     },
36432     
36433     afterSlide : function(){
36434         if(Roo.isGecko){// firefox overflow auto bug workaround
36435             this.bodyEl.unclip();
36436             if(this.tabs) {
36437                 this.tabs.bodyEl.unclip();
36438             }
36439             if(this.activePanel){
36440                 this.activePanel.getEl().unclip();
36441                 if(this.activePanel.afterSlide){
36442                     this.activePanel.afterSlide();
36443                 }
36444             }
36445         }
36446     },
36447
36448     initAutoHide : function(){
36449         if(this.autoHide !== false){
36450             if(!this.autoHideHd){
36451                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36452                 this.autoHideHd = {
36453                     "mouseout": function(e){
36454                         if(!e.within(this.el, true)){
36455                             st.delay(500);
36456                         }
36457                     },
36458                     "mouseover" : function(e){
36459                         st.cancel();
36460                     },
36461                     scope : this
36462                 };
36463             }
36464             this.el.on(this.autoHideHd);
36465         }
36466     },
36467
36468     clearAutoHide : function(){
36469         if(this.autoHide !== false){
36470             this.el.un("mouseout", this.autoHideHd.mouseout);
36471             this.el.un("mouseover", this.autoHideHd.mouseover);
36472         }
36473     },
36474
36475     clearMonitor : function(){
36476         Roo.get(document).un("click", this.slideInIf, this);
36477     },
36478
36479     // these names are backwards but not changed for compat
36480     slideOut : function(){
36481         if(this.isSlid || this.el.hasActiveFx()){
36482             return;
36483         }
36484         this.isSlid = true;
36485         if(this.collapseBtn){
36486             this.collapseBtn.hide();
36487         }
36488         this.closeBtnState = this.closeBtn.getStyle('display');
36489         this.closeBtn.hide();
36490         if(this.stickBtn){
36491             this.stickBtn.show();
36492         }
36493         this.el.show();
36494         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36495         this.beforeSlide();
36496         this.el.setStyle("z-index", 10001);
36497         this.el.slideIn(this.getSlideAnchor(), {
36498             callback: function(){
36499                 this.afterSlide();
36500                 this.initAutoHide();
36501                 Roo.get(document).on("click", this.slideInIf, this);
36502                 this.fireEvent("slideshow", this);
36503             },
36504             scope: this,
36505             block: true
36506         });
36507     },
36508
36509     afterSlideIn : function(){
36510         this.clearAutoHide();
36511         this.isSlid = false;
36512         this.clearMonitor();
36513         this.el.setStyle("z-index", "");
36514         if(this.collapseBtn){
36515             this.collapseBtn.show();
36516         }
36517         this.closeBtn.setStyle('display', this.closeBtnState);
36518         if(this.stickBtn){
36519             this.stickBtn.hide();
36520         }
36521         this.fireEvent("slidehide", this);
36522     },
36523
36524     slideIn : function(cb){
36525         if(!this.isSlid || this.el.hasActiveFx()){
36526             Roo.callback(cb);
36527             return;
36528         }
36529         this.isSlid = false;
36530         this.beforeSlide();
36531         this.el.slideOut(this.getSlideAnchor(), {
36532             callback: function(){
36533                 this.el.setLeftTop(-10000, -10000);
36534                 this.afterSlide();
36535                 this.afterSlideIn();
36536                 Roo.callback(cb);
36537             },
36538             scope: this,
36539             block: true
36540         });
36541     },
36542     
36543     slideInIf : function(e){
36544         if(!e.within(this.el)){
36545             this.slideIn();
36546         }
36547     },
36548
36549     animateCollapse : function(){
36550         this.beforeSlide();
36551         this.el.setStyle("z-index", 20000);
36552         var anchor = this.getSlideAnchor();
36553         this.el.slideOut(anchor, {
36554             callback : function(){
36555                 this.el.setStyle("z-index", "");
36556                 this.collapsedEl.slideIn(anchor, {duration:.3});
36557                 this.afterSlide();
36558                 this.el.setLocation(-10000,-10000);
36559                 this.el.hide();
36560                 this.fireEvent("collapsed", this);
36561             },
36562             scope: this,
36563             block: true
36564         });
36565     },
36566
36567     animateExpand : function(){
36568         this.beforeSlide();
36569         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36570         this.el.setStyle("z-index", 20000);
36571         this.collapsedEl.hide({
36572             duration:.1
36573         });
36574         this.el.slideIn(this.getSlideAnchor(), {
36575             callback : function(){
36576                 this.el.setStyle("z-index", "");
36577                 this.afterSlide();
36578                 if(this.split){
36579                     this.split.el.show();
36580                 }
36581                 this.fireEvent("invalidated", this);
36582                 this.fireEvent("expanded", this);
36583             },
36584             scope: this,
36585             block: true
36586         });
36587     },
36588
36589     anchors : {
36590         "west" : "left",
36591         "east" : "right",
36592         "north" : "top",
36593         "south" : "bottom"
36594     },
36595
36596     sanchors : {
36597         "west" : "l",
36598         "east" : "r",
36599         "north" : "t",
36600         "south" : "b"
36601     },
36602
36603     canchors : {
36604         "west" : "tl-tr",
36605         "east" : "tr-tl",
36606         "north" : "tl-bl",
36607         "south" : "bl-tl"
36608     },
36609
36610     getAnchor : function(){
36611         return this.anchors[this.position];
36612     },
36613
36614     getCollapseAnchor : function(){
36615         return this.canchors[this.position];
36616     },
36617
36618     getSlideAnchor : function(){
36619         return this.sanchors[this.position];
36620     },
36621
36622     getAlignAdj : function(){
36623         var cm = this.cmargins;
36624         switch(this.position){
36625             case "west":
36626                 return [0, 0];
36627             break;
36628             case "east":
36629                 return [0, 0];
36630             break;
36631             case "north":
36632                 return [0, 0];
36633             break;
36634             case "south":
36635                 return [0, 0];
36636             break;
36637         }
36638     },
36639
36640     getExpandAdj : function(){
36641         var c = this.collapsedEl, cm = this.cmargins;
36642         switch(this.position){
36643             case "west":
36644                 return [-(cm.right+c.getWidth()+cm.left), 0];
36645             break;
36646             case "east":
36647                 return [cm.right+c.getWidth()+cm.left, 0];
36648             break;
36649             case "north":
36650                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36651             break;
36652             case "south":
36653                 return [0, cm.top+cm.bottom+c.getHeight()];
36654             break;
36655         }
36656     }
36657 });/*
36658  * Based on:
36659  * Ext JS Library 1.1.1
36660  * Copyright(c) 2006-2007, Ext JS, LLC.
36661  *
36662  * Originally Released Under LGPL - original licence link has changed is not relivant.
36663  *
36664  * Fork - LGPL
36665  * <script type="text/javascript">
36666  */
36667 /*
36668  * These classes are private internal classes
36669  */
36670 Roo.bootstrap.layout.Center = function(config){
36671     config.region = "center";
36672     Roo.bootstrap.layout.Region.call(this, config);
36673     this.visible = true;
36674     this.minWidth = config.minWidth || 20;
36675     this.minHeight = config.minHeight || 20;
36676 };
36677
36678 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36679     hide : function(){
36680         // center panel can't be hidden
36681     },
36682     
36683     show : function(){
36684         // center panel can't be hidden
36685     },
36686     
36687     getMinWidth: function(){
36688         return this.minWidth;
36689     },
36690     
36691     getMinHeight: function(){
36692         return this.minHeight;
36693     }
36694 });
36695
36696
36697
36698
36699  
36700
36701
36702
36703
36704
36705 Roo.bootstrap.layout.North = function(config)
36706 {
36707     config.region = 'north';
36708     config.cursor = 'n-resize';
36709     
36710     Roo.bootstrap.layout.Split.call(this, config);
36711     
36712     
36713     if(this.split){
36714         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36715         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36716         this.split.el.addClass("roo-layout-split-v");
36717     }
36718     var size = config.initialSize || config.height;
36719     if(typeof size != "undefined"){
36720         this.el.setHeight(size);
36721     }
36722 };
36723 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36724 {
36725     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36726     
36727     
36728     
36729     getBox : function(){
36730         if(this.collapsed){
36731             return this.collapsedEl.getBox();
36732         }
36733         var box = this.el.getBox();
36734         if(this.split){
36735             box.height += this.split.el.getHeight();
36736         }
36737         return box;
36738     },
36739     
36740     updateBox : function(box){
36741         if(this.split && !this.collapsed){
36742             box.height -= this.split.el.getHeight();
36743             this.split.el.setLeft(box.x);
36744             this.split.el.setTop(box.y+box.height);
36745             this.split.el.setWidth(box.width);
36746         }
36747         if(this.collapsed){
36748             this.updateBody(box.width, null);
36749         }
36750         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36751     }
36752 });
36753
36754
36755
36756
36757
36758 Roo.bootstrap.layout.South = function(config){
36759     config.region = 'south';
36760     config.cursor = 's-resize';
36761     Roo.bootstrap.layout.Split.call(this, config);
36762     if(this.split){
36763         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36764         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36765         this.split.el.addClass("roo-layout-split-v");
36766     }
36767     var size = config.initialSize || config.height;
36768     if(typeof size != "undefined"){
36769         this.el.setHeight(size);
36770     }
36771 };
36772
36773 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36774     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36775     getBox : function(){
36776         if(this.collapsed){
36777             return this.collapsedEl.getBox();
36778         }
36779         var box = this.el.getBox();
36780         if(this.split){
36781             var sh = this.split.el.getHeight();
36782             box.height += sh;
36783             box.y -= sh;
36784         }
36785         return box;
36786     },
36787     
36788     updateBox : function(box){
36789         if(this.split && !this.collapsed){
36790             var sh = this.split.el.getHeight();
36791             box.height -= sh;
36792             box.y += sh;
36793             this.split.el.setLeft(box.x);
36794             this.split.el.setTop(box.y-sh);
36795             this.split.el.setWidth(box.width);
36796         }
36797         if(this.collapsed){
36798             this.updateBody(box.width, null);
36799         }
36800         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36801     }
36802 });
36803
36804 Roo.bootstrap.layout.East = function(config){
36805     config.region = "east";
36806     config.cursor = "e-resize";
36807     Roo.bootstrap.layout.Split.call(this, config);
36808     if(this.split){
36809         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36810         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36811         this.split.el.addClass("roo-layout-split-h");
36812     }
36813     var size = config.initialSize || config.width;
36814     if(typeof size != "undefined"){
36815         this.el.setWidth(size);
36816     }
36817 };
36818 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36819     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36820     getBox : function(){
36821         if(this.collapsed){
36822             return this.collapsedEl.getBox();
36823         }
36824         var box = this.el.getBox();
36825         if(this.split){
36826             var sw = this.split.el.getWidth();
36827             box.width += sw;
36828             box.x -= sw;
36829         }
36830         return box;
36831     },
36832
36833     updateBox : function(box){
36834         if(this.split && !this.collapsed){
36835             var sw = this.split.el.getWidth();
36836             box.width -= sw;
36837             this.split.el.setLeft(box.x);
36838             this.split.el.setTop(box.y);
36839             this.split.el.setHeight(box.height);
36840             box.x += sw;
36841         }
36842         if(this.collapsed){
36843             this.updateBody(null, box.height);
36844         }
36845         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36846     }
36847 });
36848
36849 Roo.bootstrap.layout.West = function(config){
36850     config.region = "west";
36851     config.cursor = "w-resize";
36852     
36853     Roo.bootstrap.layout.Split.call(this, config);
36854     if(this.split){
36855         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36856         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36857         this.split.el.addClass("roo-layout-split-h");
36858     }
36859     
36860 };
36861 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36862     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36863     
36864     onRender: function(ctr, pos)
36865     {
36866         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36867         var size = this.config.initialSize || this.config.width;
36868         if(typeof size != "undefined"){
36869             this.el.setWidth(size);
36870         }
36871     },
36872     
36873     getBox : function(){
36874         if(this.collapsed){
36875             return this.collapsedEl.getBox();
36876         }
36877         var box = this.el.getBox();
36878         if(this.split){
36879             box.width += this.split.el.getWidth();
36880         }
36881         return box;
36882     },
36883     
36884     updateBox : function(box){
36885         if(this.split && !this.collapsed){
36886             var sw = this.split.el.getWidth();
36887             box.width -= sw;
36888             this.split.el.setLeft(box.x+box.width);
36889             this.split.el.setTop(box.y);
36890             this.split.el.setHeight(box.height);
36891         }
36892         if(this.collapsed){
36893             this.updateBody(null, box.height);
36894         }
36895         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36896     }
36897 });
36898 Roo.namespace("Roo.bootstrap.panel");/*
36899  * Based on:
36900  * Ext JS Library 1.1.1
36901  * Copyright(c) 2006-2007, Ext JS, LLC.
36902  *
36903  * Originally Released Under LGPL - original licence link has changed is not relivant.
36904  *
36905  * Fork - LGPL
36906  * <script type="text/javascript">
36907  */
36908 /**
36909  * @class Roo.ContentPanel
36910  * @extends Roo.util.Observable
36911  * A basic ContentPanel element.
36912  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36913  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36914  * @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
36915  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36916  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36917  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36918  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36919  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36920  * @cfg {String} title          The title for this panel
36921  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36922  * @cfg {String} url            Calls {@link #setUrl} with this value
36923  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36924  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36925  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36926  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36927  * @cfg {Boolean} badges render the badges
36928
36929  * @constructor
36930  * Create a new ContentPanel.
36931  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36932  * @param {String/Object} config A string to set only the title or a config object
36933  * @param {String} content (optional) Set the HTML content for this panel
36934  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36935  */
36936 Roo.bootstrap.panel.Content = function( config){
36937     
36938     this.tpl = config.tpl || false;
36939     
36940     var el = config.el;
36941     var content = config.content;
36942
36943     if(config.autoCreate){ // xtype is available if this is called from factory
36944         el = Roo.id();
36945     }
36946     this.el = Roo.get(el);
36947     if(!this.el && config && config.autoCreate){
36948         if(typeof config.autoCreate == "object"){
36949             if(!config.autoCreate.id){
36950                 config.autoCreate.id = config.id||el;
36951             }
36952             this.el = Roo.DomHelper.append(document.body,
36953                         config.autoCreate, true);
36954         }else{
36955             var elcfg =  {   tag: "div",
36956                             cls: "roo-layout-inactive-content",
36957                             id: config.id||el
36958                             };
36959             if (config.html) {
36960                 elcfg.html = config.html;
36961                 
36962             }
36963                         
36964             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36965         }
36966     } 
36967     this.closable = false;
36968     this.loaded = false;
36969     this.active = false;
36970    
36971       
36972     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36973         
36974         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36975         
36976         this.wrapEl = this.el; //this.el.wrap();
36977         var ti = [];
36978         if (config.toolbar.items) {
36979             ti = config.toolbar.items ;
36980             delete config.toolbar.items ;
36981         }
36982         
36983         var nitems = [];
36984         this.toolbar.render(this.wrapEl, 'before');
36985         for(var i =0;i < ti.length;i++) {
36986           //  Roo.log(['add child', items[i]]);
36987             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36988         }
36989         this.toolbar.items = nitems;
36990         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36991         delete config.toolbar;
36992         
36993     }
36994     /*
36995     // xtype created footer. - not sure if will work as we normally have to render first..
36996     if (this.footer && !this.footer.el && this.footer.xtype) {
36997         if (!this.wrapEl) {
36998             this.wrapEl = this.el.wrap();
36999         }
37000     
37001         this.footer.container = this.wrapEl.createChild();
37002          
37003         this.footer = Roo.factory(this.footer, Roo);
37004         
37005     }
37006     */
37007     
37008      if(typeof config == "string"){
37009         this.title = config;
37010     }else{
37011         Roo.apply(this, config);
37012     }
37013     
37014     if(this.resizeEl){
37015         this.resizeEl = Roo.get(this.resizeEl, true);
37016     }else{
37017         this.resizeEl = this.el;
37018     }
37019     // handle view.xtype
37020     
37021  
37022     
37023     
37024     this.addEvents({
37025         /**
37026          * @event activate
37027          * Fires when this panel is activated. 
37028          * @param {Roo.ContentPanel} this
37029          */
37030         "activate" : true,
37031         /**
37032          * @event deactivate
37033          * Fires when this panel is activated. 
37034          * @param {Roo.ContentPanel} this
37035          */
37036         "deactivate" : true,
37037
37038         /**
37039          * @event resize
37040          * Fires when this panel is resized if fitToFrame is true.
37041          * @param {Roo.ContentPanel} this
37042          * @param {Number} width The width after any component adjustments
37043          * @param {Number} height The height after any component adjustments
37044          */
37045         "resize" : true,
37046         
37047          /**
37048          * @event render
37049          * Fires when this tab is created
37050          * @param {Roo.ContentPanel} this
37051          */
37052         "render" : true
37053         
37054         
37055         
37056     });
37057     
37058
37059     
37060     
37061     if(this.autoScroll){
37062         this.resizeEl.setStyle("overflow", "auto");
37063     } else {
37064         // fix randome scrolling
37065         //this.el.on('scroll', function() {
37066         //    Roo.log('fix random scolling');
37067         //    this.scrollTo('top',0); 
37068         //});
37069     }
37070     content = content || this.content;
37071     if(content){
37072         this.setContent(content);
37073     }
37074     if(config && config.url){
37075         this.setUrl(this.url, this.params, this.loadOnce);
37076     }
37077     
37078     
37079     
37080     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37081     
37082     if (this.view && typeof(this.view.xtype) != 'undefined') {
37083         this.view.el = this.el.appendChild(document.createElement("div"));
37084         this.view = Roo.factory(this.view); 
37085         this.view.render  &&  this.view.render(false, '');  
37086     }
37087     
37088     
37089     this.fireEvent('render', this);
37090 };
37091
37092 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37093     
37094     tabTip : '',
37095     
37096     setRegion : function(region){
37097         this.region = region;
37098         this.setActiveClass(region && !this.background);
37099     },
37100     
37101     
37102     setActiveClass: function(state)
37103     {
37104         if(state){
37105            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37106            this.el.setStyle('position','relative');
37107         }else{
37108            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37109            this.el.setStyle('position', 'absolute');
37110         } 
37111     },
37112     
37113     /**
37114      * Returns the toolbar for this Panel if one was configured. 
37115      * @return {Roo.Toolbar} 
37116      */
37117     getToolbar : function(){
37118         return this.toolbar;
37119     },
37120     
37121     setActiveState : function(active)
37122     {
37123         this.active = active;
37124         this.setActiveClass(active);
37125         if(!active){
37126             if(this.fireEvent("deactivate", this) === false){
37127                 return false;
37128             }
37129             return true;
37130         }
37131         this.fireEvent("activate", this);
37132         return true;
37133     },
37134     /**
37135      * Updates this panel's element
37136      * @param {String} content The new content
37137      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37138     */
37139     setContent : function(content, loadScripts){
37140         this.el.update(content, loadScripts);
37141     },
37142
37143     ignoreResize : function(w, h){
37144         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37145             return true;
37146         }else{
37147             this.lastSize = {width: w, height: h};
37148             return false;
37149         }
37150     },
37151     /**
37152      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37153      * @return {Roo.UpdateManager} The UpdateManager
37154      */
37155     getUpdateManager : function(){
37156         return this.el.getUpdateManager();
37157     },
37158      /**
37159      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37160      * @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:
37161 <pre><code>
37162 panel.load({
37163     url: "your-url.php",
37164     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37165     callback: yourFunction,
37166     scope: yourObject, //(optional scope)
37167     discardUrl: false,
37168     nocache: false,
37169     text: "Loading...",
37170     timeout: 30,
37171     scripts: false
37172 });
37173 </code></pre>
37174      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37175      * 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.
37176      * @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}
37177      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37178      * @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.
37179      * @return {Roo.ContentPanel} this
37180      */
37181     load : function(){
37182         var um = this.el.getUpdateManager();
37183         um.update.apply(um, arguments);
37184         return this;
37185     },
37186
37187
37188     /**
37189      * 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.
37190      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37191      * @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)
37192      * @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)
37193      * @return {Roo.UpdateManager} The UpdateManager
37194      */
37195     setUrl : function(url, params, loadOnce){
37196         if(this.refreshDelegate){
37197             this.removeListener("activate", this.refreshDelegate);
37198         }
37199         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37200         this.on("activate", this.refreshDelegate);
37201         return this.el.getUpdateManager();
37202     },
37203     
37204     _handleRefresh : function(url, params, loadOnce){
37205         if(!loadOnce || !this.loaded){
37206             var updater = this.el.getUpdateManager();
37207             updater.update(url, params, this._setLoaded.createDelegate(this));
37208         }
37209     },
37210     
37211     _setLoaded : function(){
37212         this.loaded = true;
37213     }, 
37214     
37215     /**
37216      * Returns this panel's id
37217      * @return {String} 
37218      */
37219     getId : function(){
37220         return this.el.id;
37221     },
37222     
37223     /** 
37224      * Returns this panel's element - used by regiosn to add.
37225      * @return {Roo.Element} 
37226      */
37227     getEl : function(){
37228         return this.wrapEl || this.el;
37229     },
37230     
37231    
37232     
37233     adjustForComponents : function(width, height)
37234     {
37235         //Roo.log('adjustForComponents ');
37236         if(this.resizeEl != this.el){
37237             width -= this.el.getFrameWidth('lr');
37238             height -= this.el.getFrameWidth('tb');
37239         }
37240         if(this.toolbar){
37241             var te = this.toolbar.getEl();
37242             te.setWidth(width);
37243             height -= te.getHeight();
37244         }
37245         if(this.footer){
37246             var te = this.footer.getEl();
37247             te.setWidth(width);
37248             height -= te.getHeight();
37249         }
37250         
37251         
37252         if(this.adjustments){
37253             width += this.adjustments[0];
37254             height += this.adjustments[1];
37255         }
37256         return {"width": width, "height": height};
37257     },
37258     
37259     setSize : function(width, height){
37260         if(this.fitToFrame && !this.ignoreResize(width, height)){
37261             if(this.fitContainer && this.resizeEl != this.el){
37262                 this.el.setSize(width, height);
37263             }
37264             var size = this.adjustForComponents(width, height);
37265             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37266             this.fireEvent('resize', this, size.width, size.height);
37267         }
37268     },
37269     
37270     /**
37271      * Returns this panel's title
37272      * @return {String} 
37273      */
37274     getTitle : function(){
37275         
37276         if (typeof(this.title) != 'object') {
37277             return this.title;
37278         }
37279         
37280         var t = '';
37281         for (var k in this.title) {
37282             if (!this.title.hasOwnProperty(k)) {
37283                 continue;
37284             }
37285             
37286             if (k.indexOf('-') >= 0) {
37287                 var s = k.split('-');
37288                 for (var i = 0; i<s.length; i++) {
37289                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37290                 }
37291             } else {
37292                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37293             }
37294         }
37295         return t;
37296     },
37297     
37298     /**
37299      * Set this panel's title
37300      * @param {String} title
37301      */
37302     setTitle : function(title){
37303         this.title = title;
37304         if(this.region){
37305             this.region.updatePanelTitle(this, title);
37306         }
37307     },
37308     
37309     /**
37310      * Returns true is this panel was configured to be closable
37311      * @return {Boolean} 
37312      */
37313     isClosable : function(){
37314         return this.closable;
37315     },
37316     
37317     beforeSlide : function(){
37318         this.el.clip();
37319         this.resizeEl.clip();
37320     },
37321     
37322     afterSlide : function(){
37323         this.el.unclip();
37324         this.resizeEl.unclip();
37325     },
37326     
37327     /**
37328      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37329      *   Will fail silently if the {@link #setUrl} method has not been called.
37330      *   This does not activate the panel, just updates its content.
37331      */
37332     refresh : function(){
37333         if(this.refreshDelegate){
37334            this.loaded = false;
37335            this.refreshDelegate();
37336         }
37337     },
37338     
37339     /**
37340      * Destroys this panel
37341      */
37342     destroy : function(){
37343         this.el.removeAllListeners();
37344         var tempEl = document.createElement("span");
37345         tempEl.appendChild(this.el.dom);
37346         tempEl.innerHTML = "";
37347         this.el.remove();
37348         this.el = null;
37349     },
37350     
37351     /**
37352      * form - if the content panel contains a form - this is a reference to it.
37353      * @type {Roo.form.Form}
37354      */
37355     form : false,
37356     /**
37357      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37358      *    This contains a reference to it.
37359      * @type {Roo.View}
37360      */
37361     view : false,
37362     
37363       /**
37364      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37365      * <pre><code>
37366
37367 layout.addxtype({
37368        xtype : 'Form',
37369        items: [ .... ]
37370    }
37371 );
37372
37373 </code></pre>
37374      * @param {Object} cfg Xtype definition of item to add.
37375      */
37376     
37377     
37378     getChildContainer: function () {
37379         return this.getEl();
37380     }
37381     
37382     
37383     /*
37384         var  ret = new Roo.factory(cfg);
37385         return ret;
37386         
37387         
37388         // add form..
37389         if (cfg.xtype.match(/^Form$/)) {
37390             
37391             var el;
37392             //if (this.footer) {
37393             //    el = this.footer.container.insertSibling(false, 'before');
37394             //} else {
37395                 el = this.el.createChild();
37396             //}
37397
37398             this.form = new  Roo.form.Form(cfg);
37399             
37400             
37401             if ( this.form.allItems.length) {
37402                 this.form.render(el.dom);
37403             }
37404             return this.form;
37405         }
37406         // should only have one of theses..
37407         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37408             // views.. should not be just added - used named prop 'view''
37409             
37410             cfg.el = this.el.appendChild(document.createElement("div"));
37411             // factory?
37412             
37413             var ret = new Roo.factory(cfg);
37414              
37415              ret.render && ret.render(false, ''); // render blank..
37416             this.view = ret;
37417             return ret;
37418         }
37419         return false;
37420     }
37421     \*/
37422 });
37423  
37424 /**
37425  * @class Roo.bootstrap.panel.Grid
37426  * @extends Roo.bootstrap.panel.Content
37427  * @constructor
37428  * Create a new GridPanel.
37429  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37430  * @param {Object} config A the config object
37431   
37432  */
37433
37434
37435
37436 Roo.bootstrap.panel.Grid = function(config)
37437 {
37438     
37439       
37440     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37441         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37442
37443     config.el = this.wrapper;
37444     //this.el = this.wrapper;
37445     
37446       if (config.container) {
37447         // ctor'ed from a Border/panel.grid
37448         
37449         
37450         this.wrapper.setStyle("overflow", "hidden");
37451         this.wrapper.addClass('roo-grid-container');
37452
37453     }
37454     
37455     
37456     if(config.toolbar){
37457         var tool_el = this.wrapper.createChild();    
37458         this.toolbar = Roo.factory(config.toolbar);
37459         var ti = [];
37460         if (config.toolbar.items) {
37461             ti = config.toolbar.items ;
37462             delete config.toolbar.items ;
37463         }
37464         
37465         var nitems = [];
37466         this.toolbar.render(tool_el);
37467         for(var i =0;i < ti.length;i++) {
37468           //  Roo.log(['add child', items[i]]);
37469             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37470         }
37471         this.toolbar.items = nitems;
37472         
37473         delete config.toolbar;
37474     }
37475     
37476     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37477     config.grid.scrollBody = true;;
37478     config.grid.monitorWindowResize = false; // turn off autosizing
37479     config.grid.autoHeight = false;
37480     config.grid.autoWidth = false;
37481     
37482     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37483     
37484     if (config.background) {
37485         // render grid on panel activation (if panel background)
37486         this.on('activate', function(gp) {
37487             if (!gp.grid.rendered) {
37488                 gp.grid.render(this.wrapper);
37489                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37490             }
37491         });
37492             
37493     } else {
37494         this.grid.render(this.wrapper);
37495         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37496
37497     }
37498     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37499     // ??? needed ??? config.el = this.wrapper;
37500     
37501     
37502     
37503   
37504     // xtype created footer. - not sure if will work as we normally have to render first..
37505     if (this.footer && !this.footer.el && this.footer.xtype) {
37506         
37507         var ctr = this.grid.getView().getFooterPanel(true);
37508         this.footer.dataSource = this.grid.dataSource;
37509         this.footer = Roo.factory(this.footer, Roo);
37510         this.footer.render(ctr);
37511         
37512     }
37513     
37514     
37515     
37516     
37517      
37518 };
37519
37520 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37521     getId : function(){
37522         return this.grid.id;
37523     },
37524     
37525     /**
37526      * Returns the grid for this panel
37527      * @return {Roo.bootstrap.Table} 
37528      */
37529     getGrid : function(){
37530         return this.grid;    
37531     },
37532     
37533     setSize : function(width, height){
37534         if(!this.ignoreResize(width, height)){
37535             var grid = this.grid;
37536             var size = this.adjustForComponents(width, height);
37537             var gridel = grid.getGridEl();
37538             gridel.setSize(size.width, size.height);
37539             /*
37540             var thd = grid.getGridEl().select('thead',true).first();
37541             var tbd = grid.getGridEl().select('tbody', true).first();
37542             if (tbd) {
37543                 tbd.setSize(width, height - thd.getHeight());
37544             }
37545             */
37546             grid.autoSize();
37547         }
37548     },
37549      
37550     
37551     
37552     beforeSlide : function(){
37553         this.grid.getView().scroller.clip();
37554     },
37555     
37556     afterSlide : function(){
37557         this.grid.getView().scroller.unclip();
37558     },
37559     
37560     destroy : function(){
37561         this.grid.destroy();
37562         delete this.grid;
37563         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37564     }
37565 });
37566
37567 /**
37568  * @class Roo.bootstrap.panel.Nest
37569  * @extends Roo.bootstrap.panel.Content
37570  * @constructor
37571  * Create a new Panel, that can contain a layout.Border.
37572  * 
37573  * 
37574  * @param {Roo.BorderLayout} layout The layout for this panel
37575  * @param {String/Object} config A string to set only the title or a config object
37576  */
37577 Roo.bootstrap.panel.Nest = function(config)
37578 {
37579     // construct with only one argument..
37580     /* FIXME - implement nicer consturctors
37581     if (layout.layout) {
37582         config = layout;
37583         layout = config.layout;
37584         delete config.layout;
37585     }
37586     if (layout.xtype && !layout.getEl) {
37587         // then layout needs constructing..
37588         layout = Roo.factory(layout, Roo);
37589     }
37590     */
37591     
37592     config.el =  config.layout.getEl();
37593     
37594     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37595     
37596     config.layout.monitorWindowResize = false; // turn off autosizing
37597     this.layout = config.layout;
37598     this.layout.getEl().addClass("roo-layout-nested-layout");
37599     
37600     
37601     
37602     
37603 };
37604
37605 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37606
37607     setSize : function(width, height){
37608         if(!this.ignoreResize(width, height)){
37609             var size = this.adjustForComponents(width, height);
37610             var el = this.layout.getEl();
37611             if (size.height < 1) {
37612                 el.setWidth(size.width);   
37613             } else {
37614                 el.setSize(size.width, size.height);
37615             }
37616             var touch = el.dom.offsetWidth;
37617             this.layout.layout();
37618             // ie requires a double layout on the first pass
37619             if(Roo.isIE && !this.initialized){
37620                 this.initialized = true;
37621                 this.layout.layout();
37622             }
37623         }
37624     },
37625     
37626     // activate all subpanels if not currently active..
37627     
37628     setActiveState : function(active){
37629         this.active = active;
37630         this.setActiveClass(active);
37631         
37632         if(!active){
37633             this.fireEvent("deactivate", this);
37634             return;
37635         }
37636         
37637         this.fireEvent("activate", this);
37638         // not sure if this should happen before or after..
37639         if (!this.layout) {
37640             return; // should not happen..
37641         }
37642         var reg = false;
37643         for (var r in this.layout.regions) {
37644             reg = this.layout.getRegion(r);
37645             if (reg.getActivePanel()) {
37646                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37647                 reg.setActivePanel(reg.getActivePanel());
37648                 continue;
37649             }
37650             if (!reg.panels.length) {
37651                 continue;
37652             }
37653             reg.showPanel(reg.getPanel(0));
37654         }
37655         
37656         
37657         
37658         
37659     },
37660     
37661     /**
37662      * Returns the nested BorderLayout for this panel
37663      * @return {Roo.BorderLayout} 
37664      */
37665     getLayout : function(){
37666         return this.layout;
37667     },
37668     
37669      /**
37670      * Adds a xtype elements to the layout of the nested panel
37671      * <pre><code>
37672
37673 panel.addxtype({
37674        xtype : 'ContentPanel',
37675        region: 'west',
37676        items: [ .... ]
37677    }
37678 );
37679
37680 panel.addxtype({
37681         xtype : 'NestedLayoutPanel',
37682         region: 'west',
37683         layout: {
37684            center: { },
37685            west: { }   
37686         },
37687         items : [ ... list of content panels or nested layout panels.. ]
37688    }
37689 );
37690 </code></pre>
37691      * @param {Object} cfg Xtype definition of item to add.
37692      */
37693     addxtype : function(cfg) {
37694         return this.layout.addxtype(cfg);
37695     
37696     }
37697 });        /*
37698  * Based on:
37699  * Ext JS Library 1.1.1
37700  * Copyright(c) 2006-2007, Ext JS, LLC.
37701  *
37702  * Originally Released Under LGPL - original licence link has changed is not relivant.
37703  *
37704  * Fork - LGPL
37705  * <script type="text/javascript">
37706  */
37707 /**
37708  * @class Roo.TabPanel
37709  * @extends Roo.util.Observable
37710  * A lightweight tab container.
37711  * <br><br>
37712  * Usage:
37713  * <pre><code>
37714 // basic tabs 1, built from existing content
37715 var tabs = new Roo.TabPanel("tabs1");
37716 tabs.addTab("script", "View Script");
37717 tabs.addTab("markup", "View Markup");
37718 tabs.activate("script");
37719
37720 // more advanced tabs, built from javascript
37721 var jtabs = new Roo.TabPanel("jtabs");
37722 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37723
37724 // set up the UpdateManager
37725 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37726 var updater = tab2.getUpdateManager();
37727 updater.setDefaultUrl("ajax1.htm");
37728 tab2.on('activate', updater.refresh, updater, true);
37729
37730 // Use setUrl for Ajax loading
37731 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37732 tab3.setUrl("ajax2.htm", null, true);
37733
37734 // Disabled tab
37735 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37736 tab4.disable();
37737
37738 jtabs.activate("jtabs-1");
37739  * </code></pre>
37740  * @constructor
37741  * Create a new TabPanel.
37742  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37743  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37744  */
37745 Roo.bootstrap.panel.Tabs = function(config){
37746     /**
37747     * The container element for this TabPanel.
37748     * @type Roo.Element
37749     */
37750     this.el = Roo.get(config.el);
37751     delete config.el;
37752     if(config){
37753         if(typeof config == "boolean"){
37754             this.tabPosition = config ? "bottom" : "top";
37755         }else{
37756             Roo.apply(this, config);
37757         }
37758     }
37759     
37760     if(this.tabPosition == "bottom"){
37761         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37762         this.el.addClass("roo-tabs-bottom");
37763     }
37764     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37765     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37766     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37767     if(Roo.isIE){
37768         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37769     }
37770     if(this.tabPosition != "bottom"){
37771         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37772          * @type Roo.Element
37773          */
37774         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37775         this.el.addClass("roo-tabs-top");
37776     }
37777     this.items = [];
37778
37779     this.bodyEl.setStyle("position", "relative");
37780
37781     this.active = null;
37782     this.activateDelegate = this.activate.createDelegate(this);
37783
37784     this.addEvents({
37785         /**
37786          * @event tabchange
37787          * Fires when the active tab changes
37788          * @param {Roo.TabPanel} this
37789          * @param {Roo.TabPanelItem} activePanel The new active tab
37790          */
37791         "tabchange": true,
37792         /**
37793          * @event beforetabchange
37794          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37795          * @param {Roo.TabPanel} this
37796          * @param {Object} e Set cancel to true on this object to cancel the tab change
37797          * @param {Roo.TabPanelItem} tab The tab being changed to
37798          */
37799         "beforetabchange" : true
37800     });
37801
37802     Roo.EventManager.onWindowResize(this.onResize, this);
37803     this.cpad = this.el.getPadding("lr");
37804     this.hiddenCount = 0;
37805
37806
37807     // toolbar on the tabbar support...
37808     if (this.toolbar) {
37809         alert("no toolbar support yet");
37810         this.toolbar  = false;
37811         /*
37812         var tcfg = this.toolbar;
37813         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37814         this.toolbar = new Roo.Toolbar(tcfg);
37815         if (Roo.isSafari) {
37816             var tbl = tcfg.container.child('table', true);
37817             tbl.setAttribute('width', '100%');
37818         }
37819         */
37820         
37821     }
37822    
37823
37824
37825     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37826 };
37827
37828 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37829     /*
37830      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37831      */
37832     tabPosition : "top",
37833     /*
37834      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37835      */
37836     currentTabWidth : 0,
37837     /*
37838      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37839      */
37840     minTabWidth : 40,
37841     /*
37842      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37843      */
37844     maxTabWidth : 250,
37845     /*
37846      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37847      */
37848     preferredTabWidth : 175,
37849     /*
37850      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37851      */
37852     resizeTabs : false,
37853     /*
37854      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37855      */
37856     monitorResize : true,
37857     /*
37858      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37859      */
37860     toolbar : false,
37861
37862     /**
37863      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37864      * @param {String} id The id of the div to use <b>or create</b>
37865      * @param {String} text The text for the tab
37866      * @param {String} content (optional) Content to put in the TabPanelItem body
37867      * @param {Boolean} closable (optional) True to create a close icon on the tab
37868      * @return {Roo.TabPanelItem} The created TabPanelItem
37869      */
37870     addTab : function(id, text, content, closable, tpl)
37871     {
37872         var item = new Roo.bootstrap.panel.TabItem({
37873             panel: this,
37874             id : id,
37875             text : text,
37876             closable : closable,
37877             tpl : tpl
37878         });
37879         this.addTabItem(item);
37880         if(content){
37881             item.setContent(content);
37882         }
37883         return item;
37884     },
37885
37886     /**
37887      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37888      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37889      * @return {Roo.TabPanelItem}
37890      */
37891     getTab : function(id){
37892         return this.items[id];
37893     },
37894
37895     /**
37896      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37897      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37898      */
37899     hideTab : function(id){
37900         var t = this.items[id];
37901         if(!t.isHidden()){
37902            t.setHidden(true);
37903            this.hiddenCount++;
37904            this.autoSizeTabs();
37905         }
37906     },
37907
37908     /**
37909      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37910      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37911      */
37912     unhideTab : function(id){
37913         var t = this.items[id];
37914         if(t.isHidden()){
37915            t.setHidden(false);
37916            this.hiddenCount--;
37917            this.autoSizeTabs();
37918         }
37919     },
37920
37921     /**
37922      * Adds an existing {@link Roo.TabPanelItem}.
37923      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37924      */
37925     addTabItem : function(item){
37926         this.items[item.id] = item;
37927         this.items.push(item);
37928       //  if(this.resizeTabs){
37929     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37930   //         this.autoSizeTabs();
37931 //        }else{
37932 //            item.autoSize();
37933        // }
37934     },
37935
37936     /**
37937      * Removes a {@link Roo.TabPanelItem}.
37938      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37939      */
37940     removeTab : function(id){
37941         var items = this.items;
37942         var tab = items[id];
37943         if(!tab) { return; }
37944         var index = items.indexOf(tab);
37945         if(this.active == tab && items.length > 1){
37946             var newTab = this.getNextAvailable(index);
37947             if(newTab) {
37948                 newTab.activate();
37949             }
37950         }
37951         this.stripEl.dom.removeChild(tab.pnode.dom);
37952         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37953             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37954         }
37955         items.splice(index, 1);
37956         delete this.items[tab.id];
37957         tab.fireEvent("close", tab);
37958         tab.purgeListeners();
37959         this.autoSizeTabs();
37960     },
37961
37962     getNextAvailable : function(start){
37963         var items = this.items;
37964         var index = start;
37965         // look for a next tab that will slide over to
37966         // replace the one being removed
37967         while(index < items.length){
37968             var item = items[++index];
37969             if(item && !item.isHidden()){
37970                 return item;
37971             }
37972         }
37973         // if one isn't found select the previous tab (on the left)
37974         index = start;
37975         while(index >= 0){
37976             var item = items[--index];
37977             if(item && !item.isHidden()){
37978                 return item;
37979             }
37980         }
37981         return null;
37982     },
37983
37984     /**
37985      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37986      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37987      */
37988     disableTab : function(id){
37989         var tab = this.items[id];
37990         if(tab && this.active != tab){
37991             tab.disable();
37992         }
37993     },
37994
37995     /**
37996      * Enables a {@link Roo.TabPanelItem} that is disabled.
37997      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37998      */
37999     enableTab : function(id){
38000         var tab = this.items[id];
38001         tab.enable();
38002     },
38003
38004     /**
38005      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38006      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38007      * @return {Roo.TabPanelItem} The TabPanelItem.
38008      */
38009     activate : function(id){
38010         var tab = this.items[id];
38011         if(!tab){
38012             return null;
38013         }
38014         if(tab == this.active || tab.disabled){
38015             return tab;
38016         }
38017         var e = {};
38018         this.fireEvent("beforetabchange", this, e, tab);
38019         if(e.cancel !== true && !tab.disabled){
38020             if(this.active){
38021                 this.active.hide();
38022             }
38023             this.active = this.items[id];
38024             this.active.show();
38025             this.fireEvent("tabchange", this, this.active);
38026         }
38027         return tab;
38028     },
38029
38030     /**
38031      * Gets the active {@link Roo.TabPanelItem}.
38032      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38033      */
38034     getActiveTab : function(){
38035         return this.active;
38036     },
38037
38038     /**
38039      * Updates the tab body element to fit the height of the container element
38040      * for overflow scrolling
38041      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38042      */
38043     syncHeight : function(targetHeight){
38044         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38045         var bm = this.bodyEl.getMargins();
38046         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38047         this.bodyEl.setHeight(newHeight);
38048         return newHeight;
38049     },
38050
38051     onResize : function(){
38052         if(this.monitorResize){
38053             this.autoSizeTabs();
38054         }
38055     },
38056
38057     /**
38058      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38059      */
38060     beginUpdate : function(){
38061         this.updating = true;
38062     },
38063
38064     /**
38065      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38066      */
38067     endUpdate : function(){
38068         this.updating = false;
38069         this.autoSizeTabs();
38070     },
38071
38072     /**
38073      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38074      */
38075     autoSizeTabs : function(){
38076         var count = this.items.length;
38077         var vcount = count - this.hiddenCount;
38078         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38079             return;
38080         }
38081         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38082         var availWidth = Math.floor(w / vcount);
38083         var b = this.stripBody;
38084         if(b.getWidth() > w){
38085             var tabs = this.items;
38086             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38087             if(availWidth < this.minTabWidth){
38088                 /*if(!this.sleft){    // incomplete scrolling code
38089                     this.createScrollButtons();
38090                 }
38091                 this.showScroll();
38092                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38093             }
38094         }else{
38095             if(this.currentTabWidth < this.preferredTabWidth){
38096                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38097             }
38098         }
38099     },
38100
38101     /**
38102      * Returns the number of tabs in this TabPanel.
38103      * @return {Number}
38104      */
38105      getCount : function(){
38106          return this.items.length;
38107      },
38108
38109     /**
38110      * Resizes all the tabs to the passed width
38111      * @param {Number} The new width
38112      */
38113     setTabWidth : function(width){
38114         this.currentTabWidth = width;
38115         for(var i = 0, len = this.items.length; i < len; i++) {
38116                 if(!this.items[i].isHidden()) {
38117                 this.items[i].setWidth(width);
38118             }
38119         }
38120     },
38121
38122     /**
38123      * Destroys this TabPanel
38124      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38125      */
38126     destroy : function(removeEl){
38127         Roo.EventManager.removeResizeListener(this.onResize, this);
38128         for(var i = 0, len = this.items.length; i < len; i++){
38129             this.items[i].purgeListeners();
38130         }
38131         if(removeEl === true){
38132             this.el.update("");
38133             this.el.remove();
38134         }
38135     },
38136     
38137     createStrip : function(container)
38138     {
38139         var strip = document.createElement("nav");
38140         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38141         container.appendChild(strip);
38142         return strip;
38143     },
38144     
38145     createStripList : function(strip)
38146     {
38147         // div wrapper for retard IE
38148         // returns the "tr" element.
38149         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38150         //'<div class="x-tabs-strip-wrap">'+
38151           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38152           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38153         return strip.firstChild; //.firstChild.firstChild.firstChild;
38154     },
38155     createBody : function(container)
38156     {
38157         var body = document.createElement("div");
38158         Roo.id(body, "tab-body");
38159         //Roo.fly(body).addClass("x-tabs-body");
38160         Roo.fly(body).addClass("tab-content");
38161         container.appendChild(body);
38162         return body;
38163     },
38164     createItemBody :function(bodyEl, id){
38165         var body = Roo.getDom(id);
38166         if(!body){
38167             body = document.createElement("div");
38168             body.id = id;
38169         }
38170         //Roo.fly(body).addClass("x-tabs-item-body");
38171         Roo.fly(body).addClass("tab-pane");
38172          bodyEl.insertBefore(body, bodyEl.firstChild);
38173         return body;
38174     },
38175     /** @private */
38176     createStripElements :  function(stripEl, text, closable, tpl)
38177     {
38178         var td = document.createElement("li"); // was td..
38179         
38180         
38181         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38182         
38183         
38184         stripEl.appendChild(td);
38185         /*if(closable){
38186             td.className = "x-tabs-closable";
38187             if(!this.closeTpl){
38188                 this.closeTpl = new Roo.Template(
38189                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38190                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38191                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38192                 );
38193             }
38194             var el = this.closeTpl.overwrite(td, {"text": text});
38195             var close = el.getElementsByTagName("div")[0];
38196             var inner = el.getElementsByTagName("em")[0];
38197             return {"el": el, "close": close, "inner": inner};
38198         } else {
38199         */
38200         // not sure what this is..
38201 //            if(!this.tabTpl){
38202                 //this.tabTpl = new Roo.Template(
38203                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38204                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38205                 //);
38206 //                this.tabTpl = new Roo.Template(
38207 //                   '<a href="#">' +
38208 //                   '<span unselectable="on"' +
38209 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38210 //                            ' >{text}</span></a>'
38211 //                );
38212 //                
38213 //            }
38214
38215
38216             var template = tpl || this.tabTpl || false;
38217             
38218             if(!template){
38219                 
38220                 template = new Roo.Template(
38221                    '<a href="#">' +
38222                    '<span unselectable="on"' +
38223                             (this.disableTooltips ? '' : ' title="{text}"') +
38224                             ' >{text}</span></a>'
38225                 );
38226             }
38227             
38228             switch (typeof(template)) {
38229                 case 'object' :
38230                     break;
38231                 case 'string' :
38232                     template = new Roo.Template(template);
38233                     break;
38234                 default :
38235                     break;
38236             }
38237             
38238             var el = template.overwrite(td, {"text": text});
38239             
38240             var inner = el.getElementsByTagName("span")[0];
38241             
38242             return {"el": el, "inner": inner};
38243             
38244     }
38245         
38246     
38247 });
38248
38249 /**
38250  * @class Roo.TabPanelItem
38251  * @extends Roo.util.Observable
38252  * Represents an individual item (tab plus body) in a TabPanel.
38253  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38254  * @param {String} id The id of this TabPanelItem
38255  * @param {String} text The text for the tab of this TabPanelItem
38256  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38257  */
38258 Roo.bootstrap.panel.TabItem = function(config){
38259     /**
38260      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38261      * @type Roo.TabPanel
38262      */
38263     this.tabPanel = config.panel;
38264     /**
38265      * The id for this TabPanelItem
38266      * @type String
38267      */
38268     this.id = config.id;
38269     /** @private */
38270     this.disabled = false;
38271     /** @private */
38272     this.text = config.text;
38273     /** @private */
38274     this.loaded = false;
38275     this.closable = config.closable;
38276
38277     /**
38278      * The body element for this TabPanelItem.
38279      * @type Roo.Element
38280      */
38281     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38282     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38283     this.bodyEl.setStyle("display", "block");
38284     this.bodyEl.setStyle("zoom", "1");
38285     //this.hideAction();
38286
38287     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38288     /** @private */
38289     this.el = Roo.get(els.el);
38290     this.inner = Roo.get(els.inner, true);
38291     this.textEl = Roo.get(this.el.dom.firstChild, true);
38292     this.pnode = Roo.get(els.el.parentNode, true);
38293 //    this.el.on("mousedown", this.onTabMouseDown, this);
38294     this.el.on("click", this.onTabClick, this);
38295     /** @private */
38296     if(config.closable){
38297         var c = Roo.get(els.close, true);
38298         c.dom.title = this.closeText;
38299         c.addClassOnOver("close-over");
38300         c.on("click", this.closeClick, this);
38301      }
38302
38303     this.addEvents({
38304          /**
38305          * @event activate
38306          * Fires when this tab becomes the active tab.
38307          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38308          * @param {Roo.TabPanelItem} this
38309          */
38310         "activate": true,
38311         /**
38312          * @event beforeclose
38313          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38314          * @param {Roo.TabPanelItem} this
38315          * @param {Object} e Set cancel to true on this object to cancel the close.
38316          */
38317         "beforeclose": true,
38318         /**
38319          * @event close
38320          * Fires when this tab is closed.
38321          * @param {Roo.TabPanelItem} this
38322          */
38323          "close": true,
38324         /**
38325          * @event deactivate
38326          * Fires when this tab is no longer the active tab.
38327          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38328          * @param {Roo.TabPanelItem} this
38329          */
38330          "deactivate" : true
38331     });
38332     this.hidden = false;
38333
38334     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38335 };
38336
38337 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38338            {
38339     purgeListeners : function(){
38340        Roo.util.Observable.prototype.purgeListeners.call(this);
38341        this.el.removeAllListeners();
38342     },
38343     /**
38344      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38345      */
38346     show : function(){
38347         this.pnode.addClass("active");
38348         this.showAction();
38349         if(Roo.isOpera){
38350             this.tabPanel.stripWrap.repaint();
38351         }
38352         this.fireEvent("activate", this.tabPanel, this);
38353     },
38354
38355     /**
38356      * Returns true if this tab is the active tab.
38357      * @return {Boolean}
38358      */
38359     isActive : function(){
38360         return this.tabPanel.getActiveTab() == this;
38361     },
38362
38363     /**
38364      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38365      */
38366     hide : function(){
38367         this.pnode.removeClass("active");
38368         this.hideAction();
38369         this.fireEvent("deactivate", this.tabPanel, this);
38370     },
38371
38372     hideAction : function(){
38373         this.bodyEl.hide();
38374         this.bodyEl.setStyle("position", "absolute");
38375         this.bodyEl.setLeft("-20000px");
38376         this.bodyEl.setTop("-20000px");
38377     },
38378
38379     showAction : function(){
38380         this.bodyEl.setStyle("position", "relative");
38381         this.bodyEl.setTop("");
38382         this.bodyEl.setLeft("");
38383         this.bodyEl.show();
38384     },
38385
38386     /**
38387      * Set the tooltip for the tab.
38388      * @param {String} tooltip The tab's tooltip
38389      */
38390     setTooltip : function(text){
38391         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38392             this.textEl.dom.qtip = text;
38393             this.textEl.dom.removeAttribute('title');
38394         }else{
38395             this.textEl.dom.title = text;
38396         }
38397     },
38398
38399     onTabClick : function(e){
38400         e.preventDefault();
38401         this.tabPanel.activate(this.id);
38402     },
38403
38404     onTabMouseDown : function(e){
38405         e.preventDefault();
38406         this.tabPanel.activate(this.id);
38407     },
38408 /*
38409     getWidth : function(){
38410         return this.inner.getWidth();
38411     },
38412
38413     setWidth : function(width){
38414         var iwidth = width - this.pnode.getPadding("lr");
38415         this.inner.setWidth(iwidth);
38416         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38417         this.pnode.setWidth(width);
38418     },
38419 */
38420     /**
38421      * Show or hide the tab
38422      * @param {Boolean} hidden True to hide or false to show.
38423      */
38424     setHidden : function(hidden){
38425         this.hidden = hidden;
38426         this.pnode.setStyle("display", hidden ? "none" : "");
38427     },
38428
38429     /**
38430      * Returns true if this tab is "hidden"
38431      * @return {Boolean}
38432      */
38433     isHidden : function(){
38434         return this.hidden;
38435     },
38436
38437     /**
38438      * Returns the text for this tab
38439      * @return {String}
38440      */
38441     getText : function(){
38442         return this.text;
38443     },
38444     /*
38445     autoSize : function(){
38446         //this.el.beginMeasure();
38447         this.textEl.setWidth(1);
38448         /*
38449          *  #2804 [new] Tabs in Roojs
38450          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38451          */
38452         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38453         //this.el.endMeasure();
38454     //},
38455
38456     /**
38457      * Sets the text for the tab (Note: this also sets the tooltip text)
38458      * @param {String} text The tab's text and tooltip
38459      */
38460     setText : function(text){
38461         this.text = text;
38462         this.textEl.update(text);
38463         this.setTooltip(text);
38464         //if(!this.tabPanel.resizeTabs){
38465         //    this.autoSize();
38466         //}
38467     },
38468     /**
38469      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38470      */
38471     activate : function(){
38472         this.tabPanel.activate(this.id);
38473     },
38474
38475     /**
38476      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38477      */
38478     disable : function(){
38479         if(this.tabPanel.active != this){
38480             this.disabled = true;
38481             this.pnode.addClass("disabled");
38482         }
38483     },
38484
38485     /**
38486      * Enables this TabPanelItem if it was previously disabled.
38487      */
38488     enable : function(){
38489         this.disabled = false;
38490         this.pnode.removeClass("disabled");
38491     },
38492
38493     /**
38494      * Sets the content for this TabPanelItem.
38495      * @param {String} content The content
38496      * @param {Boolean} loadScripts true to look for and load scripts
38497      */
38498     setContent : function(content, loadScripts){
38499         this.bodyEl.update(content, loadScripts);
38500     },
38501
38502     /**
38503      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38504      * @return {Roo.UpdateManager} The UpdateManager
38505      */
38506     getUpdateManager : function(){
38507         return this.bodyEl.getUpdateManager();
38508     },
38509
38510     /**
38511      * Set a URL to be used to load the content for this TabPanelItem.
38512      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38513      * @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)
38514      * @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)
38515      * @return {Roo.UpdateManager} The UpdateManager
38516      */
38517     setUrl : function(url, params, loadOnce){
38518         if(this.refreshDelegate){
38519             this.un('activate', this.refreshDelegate);
38520         }
38521         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38522         this.on("activate", this.refreshDelegate);
38523         return this.bodyEl.getUpdateManager();
38524     },
38525
38526     /** @private */
38527     _handleRefresh : function(url, params, loadOnce){
38528         if(!loadOnce || !this.loaded){
38529             var updater = this.bodyEl.getUpdateManager();
38530             updater.update(url, params, this._setLoaded.createDelegate(this));
38531         }
38532     },
38533
38534     /**
38535      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38536      *   Will fail silently if the setUrl method has not been called.
38537      *   This does not activate the panel, just updates its content.
38538      */
38539     refresh : function(){
38540         if(this.refreshDelegate){
38541            this.loaded = false;
38542            this.refreshDelegate();
38543         }
38544     },
38545
38546     /** @private */
38547     _setLoaded : function(){
38548         this.loaded = true;
38549     },
38550
38551     /** @private */
38552     closeClick : function(e){
38553         var o = {};
38554         e.stopEvent();
38555         this.fireEvent("beforeclose", this, o);
38556         if(o.cancel !== true){
38557             this.tabPanel.removeTab(this.id);
38558         }
38559     },
38560     /**
38561      * The text displayed in the tooltip for the close icon.
38562      * @type String
38563      */
38564     closeText : "Close this tab"
38565 });
38566 /**
38567 *    This script refer to:
38568 *    Title: International Telephone Input
38569 *    Author: Jack O'Connor
38570 *    Code version:  v12.1.12
38571 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38572 **/
38573
38574 Roo.bootstrap.PhoneInputData = function() {
38575     var d = [
38576       [
38577         "Afghanistan (‫افغانستان‬‎)",
38578         "af",
38579         "93"
38580       ],
38581       [
38582         "Albania (Shqipëri)",
38583         "al",
38584         "355"
38585       ],
38586       [
38587         "Algeria (‫الجزائر‬‎)",
38588         "dz",
38589         "213"
38590       ],
38591       [
38592         "American Samoa",
38593         "as",
38594         "1684"
38595       ],
38596       [
38597         "Andorra",
38598         "ad",
38599         "376"
38600       ],
38601       [
38602         "Angola",
38603         "ao",
38604         "244"
38605       ],
38606       [
38607         "Anguilla",
38608         "ai",
38609         "1264"
38610       ],
38611       [
38612         "Antigua and Barbuda",
38613         "ag",
38614         "1268"
38615       ],
38616       [
38617         "Argentina",
38618         "ar",
38619         "54"
38620       ],
38621       [
38622         "Armenia (Հայաստան)",
38623         "am",
38624         "374"
38625       ],
38626       [
38627         "Aruba",
38628         "aw",
38629         "297"
38630       ],
38631       [
38632         "Australia",
38633         "au",
38634         "61",
38635         0
38636       ],
38637       [
38638         "Austria (Österreich)",
38639         "at",
38640         "43"
38641       ],
38642       [
38643         "Azerbaijan (Azərbaycan)",
38644         "az",
38645         "994"
38646       ],
38647       [
38648         "Bahamas",
38649         "bs",
38650         "1242"
38651       ],
38652       [
38653         "Bahrain (‫البحرين‬‎)",
38654         "bh",
38655         "973"
38656       ],
38657       [
38658         "Bangladesh (বাংলাদেশ)",
38659         "bd",
38660         "880"
38661       ],
38662       [
38663         "Barbados",
38664         "bb",
38665         "1246"
38666       ],
38667       [
38668         "Belarus (Беларусь)",
38669         "by",
38670         "375"
38671       ],
38672       [
38673         "Belgium (België)",
38674         "be",
38675         "32"
38676       ],
38677       [
38678         "Belize",
38679         "bz",
38680         "501"
38681       ],
38682       [
38683         "Benin (Bénin)",
38684         "bj",
38685         "229"
38686       ],
38687       [
38688         "Bermuda",
38689         "bm",
38690         "1441"
38691       ],
38692       [
38693         "Bhutan (འབྲུག)",
38694         "bt",
38695         "975"
38696       ],
38697       [
38698         "Bolivia",
38699         "bo",
38700         "591"
38701       ],
38702       [
38703         "Bosnia and Herzegovina (Босна и Херцеговина)",
38704         "ba",
38705         "387"
38706       ],
38707       [
38708         "Botswana",
38709         "bw",
38710         "267"
38711       ],
38712       [
38713         "Brazil (Brasil)",
38714         "br",
38715         "55"
38716       ],
38717       [
38718         "British Indian Ocean Territory",
38719         "io",
38720         "246"
38721       ],
38722       [
38723         "British Virgin Islands",
38724         "vg",
38725         "1284"
38726       ],
38727       [
38728         "Brunei",
38729         "bn",
38730         "673"
38731       ],
38732       [
38733         "Bulgaria (България)",
38734         "bg",
38735         "359"
38736       ],
38737       [
38738         "Burkina Faso",
38739         "bf",
38740         "226"
38741       ],
38742       [
38743         "Burundi (Uburundi)",
38744         "bi",
38745         "257"
38746       ],
38747       [
38748         "Cambodia (កម្ពុជា)",
38749         "kh",
38750         "855"
38751       ],
38752       [
38753         "Cameroon (Cameroun)",
38754         "cm",
38755         "237"
38756       ],
38757       [
38758         "Canada",
38759         "ca",
38760         "1",
38761         1,
38762         ["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"]
38763       ],
38764       [
38765         "Cape Verde (Kabu Verdi)",
38766         "cv",
38767         "238"
38768       ],
38769       [
38770         "Caribbean Netherlands",
38771         "bq",
38772         "599",
38773         1
38774       ],
38775       [
38776         "Cayman Islands",
38777         "ky",
38778         "1345"
38779       ],
38780       [
38781         "Central African Republic (République centrafricaine)",
38782         "cf",
38783         "236"
38784       ],
38785       [
38786         "Chad (Tchad)",
38787         "td",
38788         "235"
38789       ],
38790       [
38791         "Chile",
38792         "cl",
38793         "56"
38794       ],
38795       [
38796         "China (中国)",
38797         "cn",
38798         "86"
38799       ],
38800       [
38801         "Christmas Island",
38802         "cx",
38803         "61",
38804         2
38805       ],
38806       [
38807         "Cocos (Keeling) Islands",
38808         "cc",
38809         "61",
38810         1
38811       ],
38812       [
38813         "Colombia",
38814         "co",
38815         "57"
38816       ],
38817       [
38818         "Comoros (‫جزر القمر‬‎)",
38819         "km",
38820         "269"
38821       ],
38822       [
38823         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38824         "cd",
38825         "243"
38826       ],
38827       [
38828         "Congo (Republic) (Congo-Brazzaville)",
38829         "cg",
38830         "242"
38831       ],
38832       [
38833         "Cook Islands",
38834         "ck",
38835         "682"
38836       ],
38837       [
38838         "Costa Rica",
38839         "cr",
38840         "506"
38841       ],
38842       [
38843         "Côte d’Ivoire",
38844         "ci",
38845         "225"
38846       ],
38847       [
38848         "Croatia (Hrvatska)",
38849         "hr",
38850         "385"
38851       ],
38852       [
38853         "Cuba",
38854         "cu",
38855         "53"
38856       ],
38857       [
38858         "Curaçao",
38859         "cw",
38860         "599",
38861         0
38862       ],
38863       [
38864         "Cyprus (Κύπρος)",
38865         "cy",
38866         "357"
38867       ],
38868       [
38869         "Czech Republic (Česká republika)",
38870         "cz",
38871         "420"
38872       ],
38873       [
38874         "Denmark (Danmark)",
38875         "dk",
38876         "45"
38877       ],
38878       [
38879         "Djibouti",
38880         "dj",
38881         "253"
38882       ],
38883       [
38884         "Dominica",
38885         "dm",
38886         "1767"
38887       ],
38888       [
38889         "Dominican Republic (República Dominicana)",
38890         "do",
38891         "1",
38892         2,
38893         ["809", "829", "849"]
38894       ],
38895       [
38896         "Ecuador",
38897         "ec",
38898         "593"
38899       ],
38900       [
38901         "Egypt (‫مصر‬‎)",
38902         "eg",
38903         "20"
38904       ],
38905       [
38906         "El Salvador",
38907         "sv",
38908         "503"
38909       ],
38910       [
38911         "Equatorial Guinea (Guinea Ecuatorial)",
38912         "gq",
38913         "240"
38914       ],
38915       [
38916         "Eritrea",
38917         "er",
38918         "291"
38919       ],
38920       [
38921         "Estonia (Eesti)",
38922         "ee",
38923         "372"
38924       ],
38925       [
38926         "Ethiopia",
38927         "et",
38928         "251"
38929       ],
38930       [
38931         "Falkland Islands (Islas Malvinas)",
38932         "fk",
38933         "500"
38934       ],
38935       [
38936         "Faroe Islands (Føroyar)",
38937         "fo",
38938         "298"
38939       ],
38940       [
38941         "Fiji",
38942         "fj",
38943         "679"
38944       ],
38945       [
38946         "Finland (Suomi)",
38947         "fi",
38948         "358",
38949         0
38950       ],
38951       [
38952         "France",
38953         "fr",
38954         "33"
38955       ],
38956       [
38957         "French Guiana (Guyane française)",
38958         "gf",
38959         "594"
38960       ],
38961       [
38962         "French Polynesia (Polynésie française)",
38963         "pf",
38964         "689"
38965       ],
38966       [
38967         "Gabon",
38968         "ga",
38969         "241"
38970       ],
38971       [
38972         "Gambia",
38973         "gm",
38974         "220"
38975       ],
38976       [
38977         "Georgia (საქართველო)",
38978         "ge",
38979         "995"
38980       ],
38981       [
38982         "Germany (Deutschland)",
38983         "de",
38984         "49"
38985       ],
38986       [
38987         "Ghana (Gaana)",
38988         "gh",
38989         "233"
38990       ],
38991       [
38992         "Gibraltar",
38993         "gi",
38994         "350"
38995       ],
38996       [
38997         "Greece (Ελλάδα)",
38998         "gr",
38999         "30"
39000       ],
39001       [
39002         "Greenland (Kalaallit Nunaat)",
39003         "gl",
39004         "299"
39005       ],
39006       [
39007         "Grenada",
39008         "gd",
39009         "1473"
39010       ],
39011       [
39012         "Guadeloupe",
39013         "gp",
39014         "590",
39015         0
39016       ],
39017       [
39018         "Guam",
39019         "gu",
39020         "1671"
39021       ],
39022       [
39023         "Guatemala",
39024         "gt",
39025         "502"
39026       ],
39027       [
39028         "Guernsey",
39029         "gg",
39030         "44",
39031         1
39032       ],
39033       [
39034         "Guinea (Guinée)",
39035         "gn",
39036         "224"
39037       ],
39038       [
39039         "Guinea-Bissau (Guiné Bissau)",
39040         "gw",
39041         "245"
39042       ],
39043       [
39044         "Guyana",
39045         "gy",
39046         "592"
39047       ],
39048       [
39049         "Haiti",
39050         "ht",
39051         "509"
39052       ],
39053       [
39054         "Honduras",
39055         "hn",
39056         "504"
39057       ],
39058       [
39059         "Hong Kong (香港)",
39060         "hk",
39061         "852"
39062       ],
39063       [
39064         "Hungary (Magyarország)",
39065         "hu",
39066         "36"
39067       ],
39068       [
39069         "Iceland (Ísland)",
39070         "is",
39071         "354"
39072       ],
39073       [
39074         "India (भारत)",
39075         "in",
39076         "91"
39077       ],
39078       [
39079         "Indonesia",
39080         "id",
39081         "62"
39082       ],
39083       [
39084         "Iran (‫ایران‬‎)",
39085         "ir",
39086         "98"
39087       ],
39088       [
39089         "Iraq (‫العراق‬‎)",
39090         "iq",
39091         "964"
39092       ],
39093       [
39094         "Ireland",
39095         "ie",
39096         "353"
39097       ],
39098       [
39099         "Isle of Man",
39100         "im",
39101         "44",
39102         2
39103       ],
39104       [
39105         "Israel (‫ישראל‬‎)",
39106         "il",
39107         "972"
39108       ],
39109       [
39110         "Italy (Italia)",
39111         "it",
39112         "39",
39113         0
39114       ],
39115       [
39116         "Jamaica",
39117         "jm",
39118         "1876"
39119       ],
39120       [
39121         "Japan (日本)",
39122         "jp",
39123         "81"
39124       ],
39125       [
39126         "Jersey",
39127         "je",
39128         "44",
39129         3
39130       ],
39131       [
39132         "Jordan (‫الأردن‬‎)",
39133         "jo",
39134         "962"
39135       ],
39136       [
39137         "Kazakhstan (Казахстан)",
39138         "kz",
39139         "7",
39140         1
39141       ],
39142       [
39143         "Kenya",
39144         "ke",
39145         "254"
39146       ],
39147       [
39148         "Kiribati",
39149         "ki",
39150         "686"
39151       ],
39152       [
39153         "Kosovo",
39154         "xk",
39155         "383"
39156       ],
39157       [
39158         "Kuwait (‫الكويت‬‎)",
39159         "kw",
39160         "965"
39161       ],
39162       [
39163         "Kyrgyzstan (Кыргызстан)",
39164         "kg",
39165         "996"
39166       ],
39167       [
39168         "Laos (ລາວ)",
39169         "la",
39170         "856"
39171       ],
39172       [
39173         "Latvia (Latvija)",
39174         "lv",
39175         "371"
39176       ],
39177       [
39178         "Lebanon (‫لبنان‬‎)",
39179         "lb",
39180         "961"
39181       ],
39182       [
39183         "Lesotho",
39184         "ls",
39185         "266"
39186       ],
39187       [
39188         "Liberia",
39189         "lr",
39190         "231"
39191       ],
39192       [
39193         "Libya (‫ليبيا‬‎)",
39194         "ly",
39195         "218"
39196       ],
39197       [
39198         "Liechtenstein",
39199         "li",
39200         "423"
39201       ],
39202       [
39203         "Lithuania (Lietuva)",
39204         "lt",
39205         "370"
39206       ],
39207       [
39208         "Luxembourg",
39209         "lu",
39210         "352"
39211       ],
39212       [
39213         "Macau (澳門)",
39214         "mo",
39215         "853"
39216       ],
39217       [
39218         "Macedonia (FYROM) (Македонија)",
39219         "mk",
39220         "389"
39221       ],
39222       [
39223         "Madagascar (Madagasikara)",
39224         "mg",
39225         "261"
39226       ],
39227       [
39228         "Malawi",
39229         "mw",
39230         "265"
39231       ],
39232       [
39233         "Malaysia",
39234         "my",
39235         "60"
39236       ],
39237       [
39238         "Maldives",
39239         "mv",
39240         "960"
39241       ],
39242       [
39243         "Mali",
39244         "ml",
39245         "223"
39246       ],
39247       [
39248         "Malta",
39249         "mt",
39250         "356"
39251       ],
39252       [
39253         "Marshall Islands",
39254         "mh",
39255         "692"
39256       ],
39257       [
39258         "Martinique",
39259         "mq",
39260         "596"
39261       ],
39262       [
39263         "Mauritania (‫موريتانيا‬‎)",
39264         "mr",
39265         "222"
39266       ],
39267       [
39268         "Mauritius (Moris)",
39269         "mu",
39270         "230"
39271       ],
39272       [
39273         "Mayotte",
39274         "yt",
39275         "262",
39276         1
39277       ],
39278       [
39279         "Mexico (México)",
39280         "mx",
39281         "52"
39282       ],
39283       [
39284         "Micronesia",
39285         "fm",
39286         "691"
39287       ],
39288       [
39289         "Moldova (Republica Moldova)",
39290         "md",
39291         "373"
39292       ],
39293       [
39294         "Monaco",
39295         "mc",
39296         "377"
39297       ],
39298       [
39299         "Mongolia (Монгол)",
39300         "mn",
39301         "976"
39302       ],
39303       [
39304         "Montenegro (Crna Gora)",
39305         "me",
39306         "382"
39307       ],
39308       [
39309         "Montserrat",
39310         "ms",
39311         "1664"
39312       ],
39313       [
39314         "Morocco (‫المغرب‬‎)",
39315         "ma",
39316         "212",
39317         0
39318       ],
39319       [
39320         "Mozambique (Moçambique)",
39321         "mz",
39322         "258"
39323       ],
39324       [
39325         "Myanmar (Burma) (မြန်မာ)",
39326         "mm",
39327         "95"
39328       ],
39329       [
39330         "Namibia (Namibië)",
39331         "na",
39332         "264"
39333       ],
39334       [
39335         "Nauru",
39336         "nr",
39337         "674"
39338       ],
39339       [
39340         "Nepal (नेपाल)",
39341         "np",
39342         "977"
39343       ],
39344       [
39345         "Netherlands (Nederland)",
39346         "nl",
39347         "31"
39348       ],
39349       [
39350         "New Caledonia (Nouvelle-Calédonie)",
39351         "nc",
39352         "687"
39353       ],
39354       [
39355         "New Zealand",
39356         "nz",
39357         "64"
39358       ],
39359       [
39360         "Nicaragua",
39361         "ni",
39362         "505"
39363       ],
39364       [
39365         "Niger (Nijar)",
39366         "ne",
39367         "227"
39368       ],
39369       [
39370         "Nigeria",
39371         "ng",
39372         "234"
39373       ],
39374       [
39375         "Niue",
39376         "nu",
39377         "683"
39378       ],
39379       [
39380         "Norfolk Island",
39381         "nf",
39382         "672"
39383       ],
39384       [
39385         "North Korea (조선 민주주의 인민 공화국)",
39386         "kp",
39387         "850"
39388       ],
39389       [
39390         "Northern Mariana Islands",
39391         "mp",
39392         "1670"
39393       ],
39394       [
39395         "Norway (Norge)",
39396         "no",
39397         "47",
39398         0
39399       ],
39400       [
39401         "Oman (‫عُمان‬‎)",
39402         "om",
39403         "968"
39404       ],
39405       [
39406         "Pakistan (‫پاکستان‬‎)",
39407         "pk",
39408         "92"
39409       ],
39410       [
39411         "Palau",
39412         "pw",
39413         "680"
39414       ],
39415       [
39416         "Palestine (‫فلسطين‬‎)",
39417         "ps",
39418         "970"
39419       ],
39420       [
39421         "Panama (Panamá)",
39422         "pa",
39423         "507"
39424       ],
39425       [
39426         "Papua New Guinea",
39427         "pg",
39428         "675"
39429       ],
39430       [
39431         "Paraguay",
39432         "py",
39433         "595"
39434       ],
39435       [
39436         "Peru (Perú)",
39437         "pe",
39438         "51"
39439       ],
39440       [
39441         "Philippines",
39442         "ph",
39443         "63"
39444       ],
39445       [
39446         "Poland (Polska)",
39447         "pl",
39448         "48"
39449       ],
39450       [
39451         "Portugal",
39452         "pt",
39453         "351"
39454       ],
39455       [
39456         "Puerto Rico",
39457         "pr",
39458         "1",
39459         3,
39460         ["787", "939"]
39461       ],
39462       [
39463         "Qatar (‫قطر‬‎)",
39464         "qa",
39465         "974"
39466       ],
39467       [
39468         "Réunion (La Réunion)",
39469         "re",
39470         "262",
39471         0
39472       ],
39473       [
39474         "Romania (România)",
39475         "ro",
39476         "40"
39477       ],
39478       [
39479         "Russia (Россия)",
39480         "ru",
39481         "7",
39482         0
39483       ],
39484       [
39485         "Rwanda",
39486         "rw",
39487         "250"
39488       ],
39489       [
39490         "Saint Barthélemy",
39491         "bl",
39492         "590",
39493         1
39494       ],
39495       [
39496         "Saint Helena",
39497         "sh",
39498         "290"
39499       ],
39500       [
39501         "Saint Kitts and Nevis",
39502         "kn",
39503         "1869"
39504       ],
39505       [
39506         "Saint Lucia",
39507         "lc",
39508         "1758"
39509       ],
39510       [
39511         "Saint Martin (Saint-Martin (partie française))",
39512         "mf",
39513         "590",
39514         2
39515       ],
39516       [
39517         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39518         "pm",
39519         "508"
39520       ],
39521       [
39522         "Saint Vincent and the Grenadines",
39523         "vc",
39524         "1784"
39525       ],
39526       [
39527         "Samoa",
39528         "ws",
39529         "685"
39530       ],
39531       [
39532         "San Marino",
39533         "sm",
39534         "378"
39535       ],
39536       [
39537         "São Tomé and Príncipe (São Tomé e Príncipe)",
39538         "st",
39539         "239"
39540       ],
39541       [
39542         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39543         "sa",
39544         "966"
39545       ],
39546       [
39547         "Senegal (Sénégal)",
39548         "sn",
39549         "221"
39550       ],
39551       [
39552         "Serbia (Србија)",
39553         "rs",
39554         "381"
39555       ],
39556       [
39557         "Seychelles",
39558         "sc",
39559         "248"
39560       ],
39561       [
39562         "Sierra Leone",
39563         "sl",
39564         "232"
39565       ],
39566       [
39567         "Singapore",
39568         "sg",
39569         "65"
39570       ],
39571       [
39572         "Sint Maarten",
39573         "sx",
39574         "1721"
39575       ],
39576       [
39577         "Slovakia (Slovensko)",
39578         "sk",
39579         "421"
39580       ],
39581       [
39582         "Slovenia (Slovenija)",
39583         "si",
39584         "386"
39585       ],
39586       [
39587         "Solomon Islands",
39588         "sb",
39589         "677"
39590       ],
39591       [
39592         "Somalia (Soomaaliya)",
39593         "so",
39594         "252"
39595       ],
39596       [
39597         "South Africa",
39598         "za",
39599         "27"
39600       ],
39601       [
39602         "South Korea (대한민국)",
39603         "kr",
39604         "82"
39605       ],
39606       [
39607         "South Sudan (‫جنوب السودان‬‎)",
39608         "ss",
39609         "211"
39610       ],
39611       [
39612         "Spain (España)",
39613         "es",
39614         "34"
39615       ],
39616       [
39617         "Sri Lanka (ශ්‍රී ලංකාව)",
39618         "lk",
39619         "94"
39620       ],
39621       [
39622         "Sudan (‫السودان‬‎)",
39623         "sd",
39624         "249"
39625       ],
39626       [
39627         "Suriname",
39628         "sr",
39629         "597"
39630       ],
39631       [
39632         "Svalbard and Jan Mayen",
39633         "sj",
39634         "47",
39635         1
39636       ],
39637       [
39638         "Swaziland",
39639         "sz",
39640         "268"
39641       ],
39642       [
39643         "Sweden (Sverige)",
39644         "se",
39645         "46"
39646       ],
39647       [
39648         "Switzerland (Schweiz)",
39649         "ch",
39650         "41"
39651       ],
39652       [
39653         "Syria (‫سوريا‬‎)",
39654         "sy",
39655         "963"
39656       ],
39657       [
39658         "Taiwan (台灣)",
39659         "tw",
39660         "886"
39661       ],
39662       [
39663         "Tajikistan",
39664         "tj",
39665         "992"
39666       ],
39667       [
39668         "Tanzania",
39669         "tz",
39670         "255"
39671       ],
39672       [
39673         "Thailand (ไทย)",
39674         "th",
39675         "66"
39676       ],
39677       [
39678         "Timor-Leste",
39679         "tl",
39680         "670"
39681       ],
39682       [
39683         "Togo",
39684         "tg",
39685         "228"
39686       ],
39687       [
39688         "Tokelau",
39689         "tk",
39690         "690"
39691       ],
39692       [
39693         "Tonga",
39694         "to",
39695         "676"
39696       ],
39697       [
39698         "Trinidad and Tobago",
39699         "tt",
39700         "1868"
39701       ],
39702       [
39703         "Tunisia (‫تونس‬‎)",
39704         "tn",
39705         "216"
39706       ],
39707       [
39708         "Turkey (Türkiye)",
39709         "tr",
39710         "90"
39711       ],
39712       [
39713         "Turkmenistan",
39714         "tm",
39715         "993"
39716       ],
39717       [
39718         "Turks and Caicos Islands",
39719         "tc",
39720         "1649"
39721       ],
39722       [
39723         "Tuvalu",
39724         "tv",
39725         "688"
39726       ],
39727       [
39728         "U.S. Virgin Islands",
39729         "vi",
39730         "1340"
39731       ],
39732       [
39733         "Uganda",
39734         "ug",
39735         "256"
39736       ],
39737       [
39738         "Ukraine (Україна)",
39739         "ua",
39740         "380"
39741       ],
39742       [
39743         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39744         "ae",
39745         "971"
39746       ],
39747       [
39748         "United Kingdom",
39749         "gb",
39750         "44",
39751         0
39752       ],
39753       [
39754         "United States",
39755         "us",
39756         "1",
39757         0
39758       ],
39759       [
39760         "Uruguay",
39761         "uy",
39762         "598"
39763       ],
39764       [
39765         "Uzbekistan (Oʻzbekiston)",
39766         "uz",
39767         "998"
39768       ],
39769       [
39770         "Vanuatu",
39771         "vu",
39772         "678"
39773       ],
39774       [
39775         "Vatican City (Città del Vaticano)",
39776         "va",
39777         "39",
39778         1
39779       ],
39780       [
39781         "Venezuela",
39782         "ve",
39783         "58"
39784       ],
39785       [
39786         "Vietnam (Việt Nam)",
39787         "vn",
39788         "84"
39789       ],
39790       [
39791         "Wallis and Futuna (Wallis-et-Futuna)",
39792         "wf",
39793         "681"
39794       ],
39795       [
39796         "Western Sahara (‫الصحراء الغربية‬‎)",
39797         "eh",
39798         "212",
39799         1
39800       ],
39801       [
39802         "Yemen (‫اليمن‬‎)",
39803         "ye",
39804         "967"
39805       ],
39806       [
39807         "Zambia",
39808         "zm",
39809         "260"
39810       ],
39811       [
39812         "Zimbabwe",
39813         "zw",
39814         "263"
39815       ],
39816       [
39817         "Åland Islands",
39818         "ax",
39819         "358",
39820         1
39821       ]
39822   ];
39823   
39824   return d;
39825 }/**
39826 *    This script refer to:
39827 *    Title: International Telephone Input
39828 *    Author: Jack O'Connor
39829 *    Code version:  v12.1.12
39830 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39831 **/
39832
39833 /**
39834  * @class Roo.bootstrap.PhoneInput
39835  * @extends Roo.bootstrap.TriggerField
39836  * An input with International dial-code selection
39837  
39838  * @cfg {String} defaultDialCode default '+852'
39839  * @cfg {Array} preferedCountries default []
39840   
39841  * @constructor
39842  * Create a new PhoneInput.
39843  * @param {Object} config Configuration options
39844  */
39845
39846 Roo.bootstrap.PhoneInput = function(config) {
39847     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39848 };
39849
39850 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39851         
39852         listWidth: undefined,
39853         
39854         selectedClass: 'active',
39855         
39856         invalidClass : "has-warning",
39857         
39858         validClass: 'has-success',
39859         
39860         allowed: '0123456789',
39861         
39862         /**
39863          * @cfg {String} defaultDialCode The default dial code when initializing the input
39864          */
39865         defaultDialCode: '+852',
39866         
39867         /**
39868          * @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
39869          */
39870         preferedCountries: false,
39871         
39872         getAutoCreate : function()
39873         {
39874             var data = Roo.bootstrap.PhoneInputData();
39875             var align = this.labelAlign || this.parentLabelAlign();
39876             var id = Roo.id();
39877             
39878             this.allCountries = [];
39879             this.dialCodeMapping = [];
39880             
39881             for (var i = 0; i < data.length; i++) {
39882               var c = data[i];
39883               this.allCountries[i] = {
39884                 name: c[0],
39885                 iso2: c[1],
39886                 dialCode: c[2],
39887                 priority: c[3] || 0,
39888                 areaCodes: c[4] || null
39889               };
39890               this.dialCodeMapping[c[2]] = {
39891                   name: c[0],
39892                   iso2: c[1],
39893                   priority: c[3] || 0,
39894                   areaCodes: c[4] || null
39895               };
39896             }
39897             
39898             var cfg = {
39899                 cls: 'form-group',
39900                 cn: []
39901             };
39902             
39903             var input =  {
39904                 tag: 'input',
39905                 id : id,
39906                 cls : 'form-control tel-input',
39907                 autocomplete: 'new-password'
39908             };
39909             
39910             var hiddenInput = {
39911                 tag: 'input',
39912                 type: 'hidden',
39913                 cls: 'hidden-tel-input'
39914             };
39915             
39916             if (this.name) {
39917                 hiddenInput.name = this.name;
39918             }
39919             
39920             if (this.disabled) {
39921                 input.disabled = true;
39922             }
39923             
39924             var flag_container = {
39925                 tag: 'div',
39926                 cls: 'flag-box',
39927                 cn: [
39928                     {
39929                         tag: 'div',
39930                         cls: 'flag'
39931                     },
39932                     {
39933                         tag: 'div',
39934                         cls: 'caret'
39935                     }
39936                 ]
39937             };
39938             
39939             var box = {
39940                 tag: 'div',
39941                 cls: this.hasFeedback ? 'has-feedback' : '',
39942                 cn: [
39943                     hiddenInput,
39944                     input,
39945                     {
39946                         tag: 'input',
39947                         cls: 'dial-code-holder',
39948                         disabled: true
39949                     }
39950                 ]
39951             };
39952             
39953             var container = {
39954                 cls: 'roo-select2-container input-group',
39955                 cn: [
39956                     flag_container,
39957                     box
39958                 ]
39959             };
39960             
39961             if (this.fieldLabel.length) {
39962                 var indicator = {
39963                     tag: 'i',
39964                     tooltip: 'This field is required'
39965                 };
39966                 
39967                 var label = {
39968                     tag: 'label',
39969                     'for':  id,
39970                     cls: 'control-label',
39971                     cn: []
39972                 };
39973                 
39974                 var label_text = {
39975                     tag: 'span',
39976                     html: this.fieldLabel
39977                 };
39978                 
39979                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39980                 label.cn = [
39981                     indicator,
39982                     label_text
39983                 ];
39984                 
39985                 if(this.indicatorpos == 'right') {
39986                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39987                     label.cn = [
39988                         label_text,
39989                         indicator
39990                     ];
39991                 }
39992                 
39993                 if(align == 'left') {
39994                     container = {
39995                         tag: 'div',
39996                         cn: [
39997                             container
39998                         ]
39999                     };
40000                     
40001                     if(this.labelWidth > 12){
40002                         label.style = "width: " + this.labelWidth + 'px';
40003                     }
40004                     if(this.labelWidth < 13 && this.labelmd == 0){
40005                         this.labelmd = this.labelWidth;
40006                     }
40007                     if(this.labellg > 0){
40008                         label.cls += ' col-lg-' + this.labellg;
40009                         input.cls += ' col-lg-' + (12 - this.labellg);
40010                     }
40011                     if(this.labelmd > 0){
40012                         label.cls += ' col-md-' + this.labelmd;
40013                         container.cls += ' col-md-' + (12 - this.labelmd);
40014                     }
40015                     if(this.labelsm > 0){
40016                         label.cls += ' col-sm-' + this.labelsm;
40017                         container.cls += ' col-sm-' + (12 - this.labelsm);
40018                     }
40019                     if(this.labelxs > 0){
40020                         label.cls += ' col-xs-' + this.labelxs;
40021                         container.cls += ' col-xs-' + (12 - this.labelxs);
40022                     }
40023                 }
40024             }
40025             
40026             cfg.cn = [
40027                 label,
40028                 container
40029             ];
40030             
40031             var settings = this;
40032             
40033             ['xs','sm','md','lg'].map(function(size){
40034                 if (settings[size]) {
40035                     cfg.cls += ' col-' + size + '-' + settings[size];
40036                 }
40037             });
40038             
40039             this.store = new Roo.data.Store({
40040                 proxy : new Roo.data.MemoryProxy({}),
40041                 reader : new Roo.data.JsonReader({
40042                     fields : [
40043                         {
40044                             'name' : 'name',
40045                             'type' : 'string'
40046                         },
40047                         {
40048                             'name' : 'iso2',
40049                             'type' : 'string'
40050                         },
40051                         {
40052                             'name' : 'dialCode',
40053                             'type' : 'string'
40054                         },
40055                         {
40056                             'name' : 'priority',
40057                             'type' : 'string'
40058                         },
40059                         {
40060                             'name' : 'areaCodes',
40061                             'type' : 'string'
40062                         }
40063                     ]
40064                 })
40065             });
40066             
40067             if(!this.preferedCountries) {
40068                 this.preferedCountries = [
40069                     'hk',
40070                     'gb',
40071                     'us'
40072                 ];
40073             }
40074             
40075             var p = this.preferedCountries.reverse();
40076             
40077             if(p) {
40078                 for (var i = 0; i < p.length; i++) {
40079                     for (var j = 0; j < this.allCountries.length; j++) {
40080                         if(this.allCountries[j].iso2 == p[i]) {
40081                             var t = this.allCountries[j];
40082                             this.allCountries.splice(j,1);
40083                             this.allCountries.unshift(t);
40084                         }
40085                     } 
40086                 }
40087             }
40088             
40089             this.store.proxy.data = {
40090                 success: true,
40091                 data: this.allCountries
40092             };
40093             
40094             return cfg;
40095         },
40096         
40097         initEvents : function()
40098         {
40099             this.createList();
40100             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40101             
40102             this.indicator = this.indicatorEl();
40103             this.flag = this.flagEl();
40104             this.dialCodeHolder = this.dialCodeHolderEl();
40105             
40106             this.trigger = this.el.select('div.flag-box',true).first();
40107             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40108             
40109             var _this = this;
40110             
40111             (function(){
40112                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40113                 _this.list.setWidth(lw);
40114             }).defer(100);
40115             
40116             this.list.on('mouseover', this.onViewOver, this);
40117             this.list.on('mousemove', this.onViewMove, this);
40118             this.inputEl().on("keyup", this.onKeyUp, this);
40119             
40120             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40121
40122             this.view = new Roo.View(this.list, this.tpl, {
40123                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40124             });
40125             
40126             this.view.on('click', this.onViewClick, this);
40127             this.setValue(this.defaultDialCode);
40128         },
40129         
40130         onTriggerClick : function(e)
40131         {
40132             Roo.log('trigger click');
40133             if(this.disabled){
40134                 return;
40135             }
40136             
40137             if(this.isExpanded()){
40138                 this.collapse();
40139                 this.hasFocus = false;
40140             }else {
40141                 this.store.load({});
40142                 this.hasFocus = true;
40143                 this.expand();
40144             }
40145         },
40146         
40147         isExpanded : function()
40148         {
40149             return this.list.isVisible();
40150         },
40151         
40152         collapse : function()
40153         {
40154             if(!this.isExpanded()){
40155                 return;
40156             }
40157             this.list.hide();
40158             Roo.get(document).un('mousedown', this.collapseIf, this);
40159             Roo.get(document).un('mousewheel', this.collapseIf, this);
40160             this.fireEvent('collapse', this);
40161             this.validate();
40162         },
40163         
40164         expand : function()
40165         {
40166             Roo.log('expand');
40167
40168             if(this.isExpanded() || !this.hasFocus){
40169                 return;
40170             }
40171             
40172             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40173             this.list.setWidth(lw);
40174             
40175             this.list.show();
40176             this.restrictHeight();
40177             
40178             Roo.get(document).on('mousedown', this.collapseIf, this);
40179             Roo.get(document).on('mousewheel', this.collapseIf, this);
40180             
40181             this.fireEvent('expand', this);
40182         },
40183         
40184         restrictHeight : function()
40185         {
40186             this.list.alignTo(this.inputEl(), this.listAlign);
40187             this.list.alignTo(this.inputEl(), this.listAlign);
40188         },
40189         
40190         onViewOver : function(e, t)
40191         {
40192             if(this.inKeyMode){
40193                 return;
40194             }
40195             var item = this.view.findItemFromChild(t);
40196             
40197             if(item){
40198                 var index = this.view.indexOf(item);
40199                 this.select(index, false);
40200             }
40201         },
40202
40203         // private
40204         onViewClick : function(view, doFocus, el, e)
40205         {
40206             var index = this.view.getSelectedIndexes()[0];
40207             
40208             var r = this.store.getAt(index);
40209             
40210             if(r){
40211                 this.onSelect(r, index);
40212             }
40213             if(doFocus !== false && !this.blockFocus){
40214                 this.inputEl().focus();
40215             }
40216         },
40217         
40218         onViewMove : function(e, t)
40219         {
40220             this.inKeyMode = false;
40221         },
40222         
40223         select : function(index, scrollIntoView)
40224         {
40225             this.selectedIndex = index;
40226             this.view.select(index);
40227             if(scrollIntoView !== false){
40228                 var el = this.view.getNode(index);
40229                 if(el){
40230                     this.list.scrollChildIntoView(el, false);
40231                 }
40232             }
40233         },
40234         
40235         createList : function()
40236         {
40237             this.list = Roo.get(document.body).createChild({
40238                 tag: 'ul',
40239                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40240                 style: 'display:none'
40241             });
40242             
40243             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40244         },
40245         
40246         collapseIf : function(e)
40247         {
40248             var in_combo  = e.within(this.el);
40249             var in_list =  e.within(this.list);
40250             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40251             
40252             if (in_combo || in_list || is_list) {
40253                 return;
40254             }
40255             this.collapse();
40256         },
40257         
40258         onSelect : function(record, index)
40259         {
40260             if(this.fireEvent('beforeselect', this, record, index) !== false){
40261                 
40262                 this.setFlagClass(record.data.iso2);
40263                 this.setDialCode(record.data.dialCode);
40264                 this.hasFocus = false;
40265                 this.collapse();
40266                 this.fireEvent('select', this, record, index);
40267             }
40268         },
40269         
40270         flagEl : function()
40271         {
40272             var flag = this.el.select('div.flag',true).first();
40273             if(!flag){
40274                 return false;
40275             }
40276             return flag;
40277         },
40278         
40279         dialCodeHolderEl : function()
40280         {
40281             var d = this.el.select('input.dial-code-holder',true).first();
40282             if(!d){
40283                 return false;
40284             }
40285             return d;
40286         },
40287         
40288         setDialCode : function(v)
40289         {
40290             this.dialCodeHolder.dom.value = '+'+v;
40291         },
40292         
40293         setFlagClass : function(n)
40294         {
40295             this.flag.dom.className = 'flag '+n;
40296         },
40297         
40298         getValue : function()
40299         {
40300             var v = this.inputEl().getValue();
40301             if(this.dialCodeHolder) {
40302                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40303             }
40304             return v;
40305         },
40306         
40307         setValue : function(v)
40308         {
40309             var d = this.getDialCode(v);
40310             
40311             //invalid dial code
40312             if(v.length == 0 || !d || d.length == 0) {
40313                 if(this.rendered){
40314                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40315                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40316                 }
40317                 return;
40318             }
40319             
40320             //valid dial code
40321             this.setFlagClass(this.dialCodeMapping[d].iso2);
40322             this.setDialCode(d);
40323             this.inputEl().dom.value = v.replace('+'+d,'');
40324             this.hiddenEl().dom.value = this.getValue();
40325             
40326             this.validate();
40327         },
40328         
40329         getDialCode : function(v)
40330         {
40331             v = v ||  '';
40332             
40333             if (v.length == 0) {
40334                 return this.dialCodeHolder.dom.value;
40335             }
40336             
40337             var dialCode = "";
40338             if (v.charAt(0) != "+") {
40339                 return false;
40340             }
40341             var numericChars = "";
40342             for (var i = 1; i < v.length; i++) {
40343               var c = v.charAt(i);
40344               if (!isNaN(c)) {
40345                 numericChars += c;
40346                 if (this.dialCodeMapping[numericChars]) {
40347                   dialCode = v.substr(1, i);
40348                 }
40349                 if (numericChars.length == 4) {
40350                   break;
40351                 }
40352               }
40353             }
40354             return dialCode;
40355         },
40356         
40357         reset : function()
40358         {
40359             this.setValue(this.defaultDialCode);
40360             this.validate();
40361         },
40362         
40363         hiddenEl : function()
40364         {
40365             return this.el.select('input.hidden-tel-input',true).first();
40366         },
40367         
40368         onKeyUp : function(e){
40369             
40370             var k = e.getKey();
40371             var c = e.getCharCode();
40372             
40373             if(
40374                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40375                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40376             ){
40377                 e.stopEvent();
40378             }
40379             
40380             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40381             //     return;
40382             // }
40383             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40384                 e.stopEvent();
40385             }
40386             
40387             this.setValue(this.getValue());
40388         }
40389         
40390 });
40391 /**
40392  * @class Roo.bootstrap.MoneyField
40393  * @extends Roo.bootstrap.ComboBox
40394  * Bootstrap MoneyField class
40395  * 
40396  * @constructor
40397  * Create a new MoneyField.
40398  * @param {Object} config Configuration options
40399  */
40400
40401 Roo.bootstrap.MoneyField = function(config) {
40402     
40403     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40404     
40405 };
40406
40407 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40408     
40409     /**
40410      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40411      */
40412     allowDecimals : true,
40413     /**
40414      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40415      */
40416     decimalSeparator : ".",
40417     /**
40418      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40419      */
40420     decimalPrecision : 0,
40421     /**
40422      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40423      */
40424     allowNegative : true,
40425     /**
40426      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40427      */
40428     allowZero: true,
40429     /**
40430      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40431      */
40432     minValue : Number.NEGATIVE_INFINITY,
40433     /**
40434      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40435      */
40436     maxValue : Number.MAX_VALUE,
40437     /**
40438      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40439      */
40440     minText : "The minimum value for this field is {0}",
40441     /**
40442      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40443      */
40444     maxText : "The maximum value for this field is {0}",
40445     /**
40446      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40447      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40448      */
40449     nanText : "{0} is not a valid number",
40450     /**
40451      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40452      */
40453     castInt : true,
40454     /**
40455      * @cfg {String} defaults currency of the MoneyField
40456      * value should be in lkey
40457      */
40458     defaultCurrency : false,
40459     /**
40460      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40461      */
40462     thousandsDelimiter : false,
40463     
40464     
40465     inputlg : 9,
40466     inputmd : 9,
40467     inputsm : 9,
40468     inputxs : 6,
40469     
40470     store : false,
40471     
40472     getAutoCreate : function()
40473     {
40474         var align = this.labelAlign || this.parentLabelAlign();
40475         
40476         var id = Roo.id();
40477
40478         var cfg = {
40479             cls: 'form-group',
40480             cn: []
40481         };
40482
40483         var input =  {
40484             tag: 'input',
40485             id : id,
40486             cls : 'form-control roo-money-amount-input',
40487             autocomplete: 'new-password'
40488         };
40489         
40490         var hiddenInput = {
40491             tag: 'input',
40492             type: 'hidden',
40493             id: Roo.id(),
40494             cls: 'hidden-number-input'
40495         };
40496         
40497         if (this.name) {
40498             hiddenInput.name = this.name;
40499         }
40500
40501         if (this.disabled) {
40502             input.disabled = true;
40503         }
40504
40505         var clg = 12 - this.inputlg;
40506         var cmd = 12 - this.inputmd;
40507         var csm = 12 - this.inputsm;
40508         var cxs = 12 - this.inputxs;
40509         
40510         var container = {
40511             tag : 'div',
40512             cls : 'row roo-money-field',
40513             cn : [
40514                 {
40515                     tag : 'div',
40516                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40517                     cn : [
40518                         {
40519                             tag : 'div',
40520                             cls: 'roo-select2-container input-group',
40521                             cn: [
40522                                 {
40523                                     tag : 'input',
40524                                     cls : 'form-control roo-money-currency-input',
40525                                     autocomplete: 'new-password',
40526                                     readOnly : 1,
40527                                     name : this.currencyName
40528                                 },
40529                                 {
40530                                     tag :'span',
40531                                     cls : 'input-group-addon',
40532                                     cn : [
40533                                         {
40534                                             tag: 'span',
40535                                             cls: 'caret'
40536                                         }
40537                                     ]
40538                                 }
40539                             ]
40540                         }
40541                     ]
40542                 },
40543                 {
40544                     tag : 'div',
40545                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40546                     cn : [
40547                         {
40548                             tag: 'div',
40549                             cls: this.hasFeedback ? 'has-feedback' : '',
40550                             cn: [
40551                                 input
40552                             ]
40553                         }
40554                     ]
40555                 }
40556             ]
40557             
40558         };
40559         
40560         if (this.fieldLabel.length) {
40561             var indicator = {
40562                 tag: 'i',
40563                 tooltip: 'This field is required'
40564             };
40565
40566             var label = {
40567                 tag: 'label',
40568                 'for':  id,
40569                 cls: 'control-label',
40570                 cn: []
40571             };
40572
40573             var label_text = {
40574                 tag: 'span',
40575                 html: this.fieldLabel
40576             };
40577
40578             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40579             label.cn = [
40580                 indicator,
40581                 label_text
40582             ];
40583
40584             if(this.indicatorpos == 'right') {
40585                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40586                 label.cn = [
40587                     label_text,
40588                     indicator
40589                 ];
40590             }
40591
40592             if(align == 'left') {
40593                 container = {
40594                     tag: 'div',
40595                     cn: [
40596                         container
40597                     ]
40598                 };
40599
40600                 if(this.labelWidth > 12){
40601                     label.style = "width: " + this.labelWidth + 'px';
40602                 }
40603                 if(this.labelWidth < 13 && this.labelmd == 0){
40604                     this.labelmd = this.labelWidth;
40605                 }
40606                 if(this.labellg > 0){
40607                     label.cls += ' col-lg-' + this.labellg;
40608                     input.cls += ' col-lg-' + (12 - this.labellg);
40609                 }
40610                 if(this.labelmd > 0){
40611                     label.cls += ' col-md-' + this.labelmd;
40612                     container.cls += ' col-md-' + (12 - this.labelmd);
40613                 }
40614                 if(this.labelsm > 0){
40615                     label.cls += ' col-sm-' + this.labelsm;
40616                     container.cls += ' col-sm-' + (12 - this.labelsm);
40617                 }
40618                 if(this.labelxs > 0){
40619                     label.cls += ' col-xs-' + this.labelxs;
40620                     container.cls += ' col-xs-' + (12 - this.labelxs);
40621                 }
40622             }
40623         }
40624
40625         cfg.cn = [
40626             label,
40627             container,
40628             hiddenInput
40629         ];
40630         
40631         var settings = this;
40632
40633         ['xs','sm','md','lg'].map(function(size){
40634             if (settings[size]) {
40635                 cfg.cls += ' col-' + size + '-' + settings[size];
40636             }
40637         });
40638         
40639         return cfg;
40640     },
40641     
40642     initEvents : function()
40643     {
40644         this.indicator = this.indicatorEl();
40645         
40646         this.initCurrencyEvent();
40647         
40648         this.initNumberEvent();
40649     },
40650     
40651     initCurrencyEvent : function()
40652     {
40653         if (!this.store) {
40654             throw "can not find store for combo";
40655         }
40656         
40657         this.store = Roo.factory(this.store, Roo.data);
40658         this.store.parent = this;
40659         
40660         this.createList();
40661         
40662         this.triggerEl = this.el.select('.input-group-addon', true).first();
40663         
40664         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40665         
40666         var _this = this;
40667         
40668         (function(){
40669             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40670             _this.list.setWidth(lw);
40671         }).defer(100);
40672         
40673         this.list.on('mouseover', this.onViewOver, this);
40674         this.list.on('mousemove', this.onViewMove, this);
40675         this.list.on('scroll', this.onViewScroll, this);
40676         
40677         if(!this.tpl){
40678             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40679         }
40680         
40681         this.view = new Roo.View(this.list, this.tpl, {
40682             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40683         });
40684         
40685         this.view.on('click', this.onViewClick, this);
40686         
40687         this.store.on('beforeload', this.onBeforeLoad, this);
40688         this.store.on('load', this.onLoad, this);
40689         this.store.on('loadexception', this.onLoadException, this);
40690         
40691         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40692             "up" : function(e){
40693                 this.inKeyMode = true;
40694                 this.selectPrev();
40695             },
40696
40697             "down" : function(e){
40698                 if(!this.isExpanded()){
40699                     this.onTriggerClick();
40700                 }else{
40701                     this.inKeyMode = true;
40702                     this.selectNext();
40703                 }
40704             },
40705
40706             "enter" : function(e){
40707                 this.collapse();
40708                 
40709                 if(this.fireEvent("specialkey", this, e)){
40710                     this.onViewClick(false);
40711                 }
40712                 
40713                 return true;
40714             },
40715
40716             "esc" : function(e){
40717                 this.collapse();
40718             },
40719
40720             "tab" : function(e){
40721                 this.collapse();
40722                 
40723                 if(this.fireEvent("specialkey", this, e)){
40724                     this.onViewClick(false);
40725                 }
40726                 
40727                 return true;
40728             },
40729
40730             scope : this,
40731
40732             doRelay : function(foo, bar, hname){
40733                 if(hname == 'down' || this.scope.isExpanded()){
40734                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40735                 }
40736                 return true;
40737             },
40738
40739             forceKeyDown: true
40740         });
40741         
40742         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40743         
40744     },
40745     
40746     initNumberEvent : function(e)
40747     {
40748         this.inputEl().on("keydown" , this.fireKey,  this);
40749         this.inputEl().on("focus", this.onFocus,  this);
40750         this.inputEl().on("blur", this.onBlur,  this);
40751         
40752         this.inputEl().relayEvent('keyup', this);
40753         
40754         if(this.indicator){
40755             this.indicator.addClass('invisible');
40756         }
40757  
40758         this.originalValue = this.getValue();
40759         
40760         if(this.validationEvent == 'keyup'){
40761             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40762             this.inputEl().on('keyup', this.filterValidation, this);
40763         }
40764         else if(this.validationEvent !== false){
40765             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40766         }
40767         
40768         if(this.selectOnFocus){
40769             this.on("focus", this.preFocus, this);
40770             
40771         }
40772         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40773             this.inputEl().on("keypress", this.filterKeys, this);
40774         } else {
40775             this.inputEl().relayEvent('keypress', this);
40776         }
40777         
40778         var allowed = "0123456789";
40779         
40780         if(this.allowDecimals){
40781             allowed += this.decimalSeparator;
40782         }
40783         
40784         if(this.allowNegative){
40785             allowed += "-";
40786         }
40787         
40788         if(this.thousandsDelimiter) {
40789             allowed += ",";
40790         }
40791         
40792         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40793         
40794         var keyPress = function(e){
40795             
40796             var k = e.getKey();
40797             
40798             var c = e.getCharCode();
40799             
40800             if(
40801                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40802                     allowed.indexOf(String.fromCharCode(c)) === -1
40803             ){
40804                 e.stopEvent();
40805                 return;
40806             }
40807             
40808             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40809                 return;
40810             }
40811             
40812             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40813                 e.stopEvent();
40814             }
40815         };
40816         
40817         this.inputEl().on("keypress", keyPress, this);
40818         
40819     },
40820     
40821     onTriggerClick : function(e)
40822     {   
40823         if(this.disabled){
40824             return;
40825         }
40826         
40827         this.page = 0;
40828         this.loadNext = false;
40829         
40830         if(this.isExpanded()){
40831             this.collapse();
40832             return;
40833         }
40834         
40835         this.hasFocus = true;
40836         
40837         if(this.triggerAction == 'all') {
40838             this.doQuery(this.allQuery, true);
40839             return;
40840         }
40841         
40842         this.doQuery(this.getRawValue());
40843     },
40844     
40845     getCurrency : function()
40846     {   
40847         var v = this.currencyEl().getValue();
40848         
40849         return v;
40850     },
40851     
40852     restrictHeight : function()
40853     {
40854         this.list.alignTo(this.currencyEl(), this.listAlign);
40855         this.list.alignTo(this.currencyEl(), this.listAlign);
40856     },
40857     
40858     onViewClick : function(view, doFocus, el, e)
40859     {
40860         var index = this.view.getSelectedIndexes()[0];
40861         
40862         var r = this.store.getAt(index);
40863         
40864         if(r){
40865             this.onSelect(r, index);
40866         }
40867     },
40868     
40869     onSelect : function(record, index){
40870         
40871         if(this.fireEvent('beforeselect', this, record, index) !== false){
40872         
40873             this.setFromCurrencyData(index > -1 ? record.data : false);
40874             
40875             this.collapse();
40876             
40877             this.fireEvent('select', this, record, index);
40878         }
40879     },
40880     
40881     setFromCurrencyData : function(o)
40882     {
40883         var currency = '';
40884         
40885         this.lastCurrency = o;
40886         
40887         if (this.currencyField) {
40888             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40889         } else {
40890             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40891         }
40892         
40893         this.lastSelectionText = currency;
40894         
40895         //setting default currency
40896         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40897             this.setCurrency(this.defaultCurrency);
40898             return;
40899         }
40900         
40901         this.setCurrency(currency);
40902     },
40903     
40904     setFromData : function(o)
40905     {
40906         var c = {};
40907         
40908         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40909         
40910         this.setFromCurrencyData(c);
40911         
40912         var value = '';
40913         
40914         if (this.name) {
40915             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40916         } else {
40917             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40918         }
40919         
40920         this.setValue(value);
40921         
40922     },
40923     
40924     setCurrency : function(v)
40925     {   
40926         this.currencyValue = v;
40927         
40928         if(this.rendered){
40929             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40930             this.validate();
40931         }
40932     },
40933     
40934     setValue : function(v)
40935     {
40936         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40937         
40938         this.value = v;
40939         
40940         if(this.rendered){
40941             
40942             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40943             
40944             this.inputEl().dom.value = (v == '') ? '' :
40945                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40946             
40947             if(!this.allowZero && v === '0') {
40948                 this.hiddenEl().dom.value = '';
40949                 this.inputEl().dom.value = '';
40950             }
40951             
40952             this.validate();
40953         }
40954     },
40955     
40956     getRawValue : function()
40957     {
40958         var v = this.inputEl().getValue();
40959         
40960         return v;
40961     },
40962     
40963     getValue : function()
40964     {
40965         return this.fixPrecision(this.parseValue(this.getRawValue()));
40966     },
40967     
40968     parseValue : function(value)
40969     {
40970         if(this.thousandsDelimiter) {
40971             value += "";
40972             r = new RegExp(",", "g");
40973             value = value.replace(r, "");
40974         }
40975         
40976         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40977         return isNaN(value) ? '' : value;
40978         
40979     },
40980     
40981     fixPrecision : function(value)
40982     {
40983         if(this.thousandsDelimiter) {
40984             value += "";
40985             r = new RegExp(",", "g");
40986             value = value.replace(r, "");
40987         }
40988         
40989         var nan = isNaN(value);
40990         
40991         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40992             return nan ? '' : value;
40993         }
40994         return parseFloat(value).toFixed(this.decimalPrecision);
40995     },
40996     
40997     decimalPrecisionFcn : function(v)
40998     {
40999         return Math.floor(v);
41000     },
41001     
41002     validateValue : function(value)
41003     {
41004         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41005             return false;
41006         }
41007         
41008         var num = this.parseValue(value);
41009         
41010         if(isNaN(num)){
41011             this.markInvalid(String.format(this.nanText, value));
41012             return false;
41013         }
41014         
41015         if(num < this.minValue){
41016             this.markInvalid(String.format(this.minText, this.minValue));
41017             return false;
41018         }
41019         
41020         if(num > this.maxValue){
41021             this.markInvalid(String.format(this.maxText, this.maxValue));
41022             return false;
41023         }
41024         
41025         return true;
41026     },
41027     
41028     validate : function()
41029     {
41030         if(this.disabled || this.allowBlank){
41031             this.markValid();
41032             return true;
41033         }
41034         
41035         var currency = this.getCurrency();
41036         
41037         if(this.validateValue(this.getRawValue()) && currency.length){
41038             this.markValid();
41039             return true;
41040         }
41041         
41042         this.markInvalid();
41043         return false;
41044     },
41045     
41046     getName: function()
41047     {
41048         return this.name;
41049     },
41050     
41051     beforeBlur : function()
41052     {
41053         if(!this.castInt){
41054             return;
41055         }
41056         
41057         var v = this.parseValue(this.getRawValue());
41058         
41059         if(v || v == 0){
41060             this.setValue(v);
41061         }
41062     },
41063     
41064     onBlur : function()
41065     {
41066         this.beforeBlur();
41067         
41068         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41069             //this.el.removeClass(this.focusClass);
41070         }
41071         
41072         this.hasFocus = false;
41073         
41074         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41075             this.validate();
41076         }
41077         
41078         var v = this.getValue();
41079         
41080         if(String(v) !== String(this.startValue)){
41081             this.fireEvent('change', this, v, this.startValue);
41082         }
41083         
41084         this.fireEvent("blur", this);
41085     },
41086     
41087     inputEl : function()
41088     {
41089         return this.el.select('.roo-money-amount-input', true).first();
41090     },
41091     
41092     currencyEl : function()
41093     {
41094         return this.el.select('.roo-money-currency-input', true).first();
41095     },
41096     
41097     hiddenEl : function()
41098     {
41099         return this.el.select('input.hidden-number-input',true).first();
41100     }
41101     
41102 });