04dbd57d059ad12bb063c05abdf8c0cfc105bcca
[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_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7116         
7117         
7118         for(var j = 0; j < w.length; j++) {
7119             
7120             if(!w[j]) {
7121                 continue;
7122             }
7123             
7124             var size_cls = w[j].split("-");
7125             
7126             if(!Number.isInteger(size_cls[1] * 1)) {
7127                 continue;
7128             }
7129             
7130             if(!this.colModel.config[col_index][size_cls[0]]) {
7131                 continue;
7132             }
7133             
7134             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7135                 continue;
7136             }
7137             
7138             h_row[0].classList.replace(
7139                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7140                 "col-"+size_cls[0]+"-"+size_cls[1]
7141             );
7142             
7143             for(var i = 0; i < rows.length; i++) {
7144                 
7145                 var size_cls = w[j].split("-");
7146                 
7147                 if(!Number.isInteger(size_cls[1] * 1)) {
7148                     continue;
7149                 }
7150                 
7151                 if(!this.colModel.config[col_index][size_cls[0]]) {
7152                     continue;
7153                 }
7154                 
7155                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7156                     continue;
7157                 }
7158                 
7159                 rows[i].classList.replace(
7160                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7161                     "col-"+size_cls[0]+"-"+size_cls[1]
7162                 );
7163             }
7164             
7165             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7166         }
7167     }
7168 });
7169
7170  
7171
7172  /*
7173  * - LGPL
7174  *
7175  * table cell
7176  * 
7177  */
7178
7179 /**
7180  * @class Roo.bootstrap.TableCell
7181  * @extends Roo.bootstrap.Component
7182  * Bootstrap TableCell class
7183  * @cfg {String} html cell contain text
7184  * @cfg {String} cls cell class
7185  * @cfg {String} tag cell tag (td|th) default td
7186  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7187  * @cfg {String} align Aligns the content in a cell
7188  * @cfg {String} axis Categorizes cells
7189  * @cfg {String} bgcolor Specifies the background color of a cell
7190  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7191  * @cfg {Number} colspan Specifies the number of columns a cell should span
7192  * @cfg {String} headers Specifies one or more header cells a cell is related to
7193  * @cfg {Number} height Sets the height of a cell
7194  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7195  * @cfg {Number} rowspan Sets the number of rows a cell should span
7196  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7197  * @cfg {String} valign Vertical aligns the content in a cell
7198  * @cfg {Number} width Specifies the width of a cell
7199  * 
7200  * @constructor
7201  * Create a new TableCell
7202  * @param {Object} config The config object
7203  */
7204
7205 Roo.bootstrap.TableCell = function(config){
7206     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7207 };
7208
7209 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7210     
7211     html: false,
7212     cls: false,
7213     tag: false,
7214     abbr: false,
7215     align: false,
7216     axis: false,
7217     bgcolor: false,
7218     charoff: false,
7219     colspan: false,
7220     headers: false,
7221     height: false,
7222     nowrap: false,
7223     rowspan: false,
7224     scope: false,
7225     valign: false,
7226     width: false,
7227     
7228     
7229     getAutoCreate : function(){
7230         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7231         
7232         cfg = {
7233             tag: 'td'
7234         };
7235         
7236         if(this.tag){
7237             cfg.tag = this.tag;
7238         }
7239         
7240         if (this.html) {
7241             cfg.html=this.html
7242         }
7243         if (this.cls) {
7244             cfg.cls=this.cls
7245         }
7246         if (this.abbr) {
7247             cfg.abbr=this.abbr
7248         }
7249         if (this.align) {
7250             cfg.align=this.align
7251         }
7252         if (this.axis) {
7253             cfg.axis=this.axis
7254         }
7255         if (this.bgcolor) {
7256             cfg.bgcolor=this.bgcolor
7257         }
7258         if (this.charoff) {
7259             cfg.charoff=this.charoff
7260         }
7261         if (this.colspan) {
7262             cfg.colspan=this.colspan
7263         }
7264         if (this.headers) {
7265             cfg.headers=this.headers
7266         }
7267         if (this.height) {
7268             cfg.height=this.height
7269         }
7270         if (this.nowrap) {
7271             cfg.nowrap=this.nowrap
7272         }
7273         if (this.rowspan) {
7274             cfg.rowspan=this.rowspan
7275         }
7276         if (this.scope) {
7277             cfg.scope=this.scope
7278         }
7279         if (this.valign) {
7280             cfg.valign=this.valign
7281         }
7282         if (this.width) {
7283             cfg.width=this.width
7284         }
7285         
7286         
7287         return cfg;
7288     }
7289    
7290 });
7291
7292  
7293
7294  /*
7295  * - LGPL
7296  *
7297  * table row
7298  * 
7299  */
7300
7301 /**
7302  * @class Roo.bootstrap.TableRow
7303  * @extends Roo.bootstrap.Component
7304  * Bootstrap TableRow class
7305  * @cfg {String} cls row class
7306  * @cfg {String} align Aligns the content in a table row
7307  * @cfg {String} bgcolor Specifies a background color for a table row
7308  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7309  * @cfg {String} valign Vertical aligns the content in a table row
7310  * 
7311  * @constructor
7312  * Create a new TableRow
7313  * @param {Object} config The config object
7314  */
7315
7316 Roo.bootstrap.TableRow = function(config){
7317     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7318 };
7319
7320 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7321     
7322     cls: false,
7323     align: false,
7324     bgcolor: false,
7325     charoff: false,
7326     valign: false,
7327     
7328     getAutoCreate : function(){
7329         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7330         
7331         cfg = {
7332             tag: 'tr'
7333         };
7334             
7335         if(this.cls){
7336             cfg.cls = this.cls;
7337         }
7338         if(this.align){
7339             cfg.align = this.align;
7340         }
7341         if(this.bgcolor){
7342             cfg.bgcolor = this.bgcolor;
7343         }
7344         if(this.charoff){
7345             cfg.charoff = this.charoff;
7346         }
7347         if(this.valign){
7348             cfg.valign = this.valign;
7349         }
7350         
7351         return cfg;
7352     }
7353    
7354 });
7355
7356  
7357
7358  /*
7359  * - LGPL
7360  *
7361  * table body
7362  * 
7363  */
7364
7365 /**
7366  * @class Roo.bootstrap.TableBody
7367  * @extends Roo.bootstrap.Component
7368  * Bootstrap TableBody class
7369  * @cfg {String} cls element class
7370  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7371  * @cfg {String} align Aligns the content inside the element
7372  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7373  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7374  * 
7375  * @constructor
7376  * Create a new TableBody
7377  * @param {Object} config The config object
7378  */
7379
7380 Roo.bootstrap.TableBody = function(config){
7381     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7382 };
7383
7384 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7385     
7386     cls: false,
7387     tag: false,
7388     align: false,
7389     charoff: false,
7390     valign: false,
7391     
7392     getAutoCreate : function(){
7393         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7394         
7395         cfg = {
7396             tag: 'tbody'
7397         };
7398             
7399         if (this.cls) {
7400             cfg.cls=this.cls
7401         }
7402         if(this.tag){
7403             cfg.tag = this.tag;
7404         }
7405         
7406         if(this.align){
7407             cfg.align = this.align;
7408         }
7409         if(this.charoff){
7410             cfg.charoff = this.charoff;
7411         }
7412         if(this.valign){
7413             cfg.valign = this.valign;
7414         }
7415         
7416         return cfg;
7417     }
7418     
7419     
7420 //    initEvents : function()
7421 //    {
7422 //        
7423 //        if(!this.store){
7424 //            return;
7425 //        }
7426 //        
7427 //        this.store = Roo.factory(this.store, Roo.data);
7428 //        this.store.on('load', this.onLoad, this);
7429 //        
7430 //        this.store.load();
7431 //        
7432 //    },
7433 //    
7434 //    onLoad: function () 
7435 //    {   
7436 //        this.fireEvent('load', this);
7437 //    }
7438 //    
7439 //   
7440 });
7441
7442  
7443
7444  /*
7445  * Based on:
7446  * Ext JS Library 1.1.1
7447  * Copyright(c) 2006-2007, Ext JS, LLC.
7448  *
7449  * Originally Released Under LGPL - original licence link has changed is not relivant.
7450  *
7451  * Fork - LGPL
7452  * <script type="text/javascript">
7453  */
7454
7455 // as we use this in bootstrap.
7456 Roo.namespace('Roo.form');
7457  /**
7458  * @class Roo.form.Action
7459  * Internal Class used to handle form actions
7460  * @constructor
7461  * @param {Roo.form.BasicForm} el The form element or its id
7462  * @param {Object} config Configuration options
7463  */
7464
7465  
7466  
7467 // define the action interface
7468 Roo.form.Action = function(form, options){
7469     this.form = form;
7470     this.options = options || {};
7471 };
7472 /**
7473  * Client Validation Failed
7474  * @const 
7475  */
7476 Roo.form.Action.CLIENT_INVALID = 'client';
7477 /**
7478  * Server Validation Failed
7479  * @const 
7480  */
7481 Roo.form.Action.SERVER_INVALID = 'server';
7482  /**
7483  * Connect to Server Failed
7484  * @const 
7485  */
7486 Roo.form.Action.CONNECT_FAILURE = 'connect';
7487 /**
7488  * Reading Data from Server Failed
7489  * @const 
7490  */
7491 Roo.form.Action.LOAD_FAILURE = 'load';
7492
7493 Roo.form.Action.prototype = {
7494     type : 'default',
7495     failureType : undefined,
7496     response : undefined,
7497     result : undefined,
7498
7499     // interface method
7500     run : function(options){
7501
7502     },
7503
7504     // interface method
7505     success : function(response){
7506
7507     },
7508
7509     // interface method
7510     handleResponse : function(response){
7511
7512     },
7513
7514     // default connection failure
7515     failure : function(response){
7516         
7517         this.response = response;
7518         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7519         this.form.afterAction(this, false);
7520     },
7521
7522     processResponse : function(response){
7523         this.response = response;
7524         if(!response.responseText){
7525             return true;
7526         }
7527         this.result = this.handleResponse(response);
7528         return this.result;
7529     },
7530
7531     // utility functions used internally
7532     getUrl : function(appendParams){
7533         var url = this.options.url || this.form.url || this.form.el.dom.action;
7534         if(appendParams){
7535             var p = this.getParams();
7536             if(p){
7537                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7538             }
7539         }
7540         return url;
7541     },
7542
7543     getMethod : function(){
7544         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7545     },
7546
7547     getParams : function(){
7548         var bp = this.form.baseParams;
7549         var p = this.options.params;
7550         if(p){
7551             if(typeof p == "object"){
7552                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7553             }else if(typeof p == 'string' && bp){
7554                 p += '&' + Roo.urlEncode(bp);
7555             }
7556         }else if(bp){
7557             p = Roo.urlEncode(bp);
7558         }
7559         return p;
7560     },
7561
7562     createCallback : function(){
7563         return {
7564             success: this.success,
7565             failure: this.failure,
7566             scope: this,
7567             timeout: (this.form.timeout*1000),
7568             upload: this.form.fileUpload ? this.success : undefined
7569         };
7570     }
7571 };
7572
7573 Roo.form.Action.Submit = function(form, options){
7574     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7575 };
7576
7577 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7578     type : 'submit',
7579
7580     haveProgress : false,
7581     uploadComplete : false,
7582     
7583     // uploadProgress indicator.
7584     uploadProgress : function()
7585     {
7586         if (!this.form.progressUrl) {
7587             return;
7588         }
7589         
7590         if (!this.haveProgress) {
7591             Roo.MessageBox.progress("Uploading", "Uploading");
7592         }
7593         if (this.uploadComplete) {
7594            Roo.MessageBox.hide();
7595            return;
7596         }
7597         
7598         this.haveProgress = true;
7599    
7600         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7601         
7602         var c = new Roo.data.Connection();
7603         c.request({
7604             url : this.form.progressUrl,
7605             params: {
7606                 id : uid
7607             },
7608             method: 'GET',
7609             success : function(req){
7610                //console.log(data);
7611                 var rdata = false;
7612                 var edata;
7613                 try  {
7614                    rdata = Roo.decode(req.responseText)
7615                 } catch (e) {
7616                     Roo.log("Invalid data from server..");
7617                     Roo.log(edata);
7618                     return;
7619                 }
7620                 if (!rdata || !rdata.success) {
7621                     Roo.log(rdata);
7622                     Roo.MessageBox.alert(Roo.encode(rdata));
7623                     return;
7624                 }
7625                 var data = rdata.data;
7626                 
7627                 if (this.uploadComplete) {
7628                    Roo.MessageBox.hide();
7629                    return;
7630                 }
7631                    
7632                 if (data){
7633                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7634                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7635                     );
7636                 }
7637                 this.uploadProgress.defer(2000,this);
7638             },
7639        
7640             failure: function(data) {
7641                 Roo.log('progress url failed ');
7642                 Roo.log(data);
7643             },
7644             scope : this
7645         });
7646            
7647     },
7648     
7649     
7650     run : function()
7651     {
7652         // run get Values on the form, so it syncs any secondary forms.
7653         this.form.getValues();
7654         
7655         var o = this.options;
7656         var method = this.getMethod();
7657         var isPost = method == 'POST';
7658         if(o.clientValidation === false || this.form.isValid()){
7659             
7660             if (this.form.progressUrl) {
7661                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7662                     (new Date() * 1) + '' + Math.random());
7663                     
7664             } 
7665             
7666             
7667             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7668                 form:this.form.el.dom,
7669                 url:this.getUrl(!isPost),
7670                 method: method,
7671                 params:isPost ? this.getParams() : null,
7672                 isUpload: this.form.fileUpload
7673             }));
7674             
7675             this.uploadProgress();
7676
7677         }else if (o.clientValidation !== false){ // client validation failed
7678             this.failureType = Roo.form.Action.CLIENT_INVALID;
7679             this.form.afterAction(this, false);
7680         }
7681     },
7682
7683     success : function(response)
7684     {
7685         this.uploadComplete= true;
7686         if (this.haveProgress) {
7687             Roo.MessageBox.hide();
7688         }
7689         
7690         
7691         var result = this.processResponse(response);
7692         if(result === true || result.success){
7693             this.form.afterAction(this, true);
7694             return;
7695         }
7696         if(result.errors){
7697             this.form.markInvalid(result.errors);
7698             this.failureType = Roo.form.Action.SERVER_INVALID;
7699         }
7700         this.form.afterAction(this, false);
7701     },
7702     failure : function(response)
7703     {
7704         this.uploadComplete= true;
7705         if (this.haveProgress) {
7706             Roo.MessageBox.hide();
7707         }
7708         
7709         this.response = response;
7710         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7711         this.form.afterAction(this, false);
7712     },
7713     
7714     handleResponse : function(response){
7715         if(this.form.errorReader){
7716             var rs = this.form.errorReader.read(response);
7717             var errors = [];
7718             if(rs.records){
7719                 for(var i = 0, len = rs.records.length; i < len; i++) {
7720                     var r = rs.records[i];
7721                     errors[i] = r.data;
7722                 }
7723             }
7724             if(errors.length < 1){
7725                 errors = null;
7726             }
7727             return {
7728                 success : rs.success,
7729                 errors : errors
7730             };
7731         }
7732         var ret = false;
7733         try {
7734             ret = Roo.decode(response.responseText);
7735         } catch (e) {
7736             ret = {
7737                 success: false,
7738                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7739                 errors : []
7740             };
7741         }
7742         return ret;
7743         
7744     }
7745 });
7746
7747
7748 Roo.form.Action.Load = function(form, options){
7749     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7750     this.reader = this.form.reader;
7751 };
7752
7753 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7754     type : 'load',
7755
7756     run : function(){
7757         
7758         Roo.Ajax.request(Roo.apply(
7759                 this.createCallback(), {
7760                     method:this.getMethod(),
7761                     url:this.getUrl(false),
7762                     params:this.getParams()
7763         }));
7764     },
7765
7766     success : function(response){
7767         
7768         var result = this.processResponse(response);
7769         if(result === true || !result.success || !result.data){
7770             this.failureType = Roo.form.Action.LOAD_FAILURE;
7771             this.form.afterAction(this, false);
7772             return;
7773         }
7774         this.form.clearInvalid();
7775         this.form.setValues(result.data);
7776         this.form.afterAction(this, true);
7777     },
7778
7779     handleResponse : function(response){
7780         if(this.form.reader){
7781             var rs = this.form.reader.read(response);
7782             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7783             return {
7784                 success : rs.success,
7785                 data : data
7786             };
7787         }
7788         return Roo.decode(response.responseText);
7789     }
7790 });
7791
7792 Roo.form.Action.ACTION_TYPES = {
7793     'load' : Roo.form.Action.Load,
7794     'submit' : Roo.form.Action.Submit
7795 };/*
7796  * - LGPL
7797  *
7798  * form
7799  *
7800  */
7801
7802 /**
7803  * @class Roo.bootstrap.Form
7804  * @extends Roo.bootstrap.Component
7805  * Bootstrap Form class
7806  * @cfg {String} method  GET | POST (default POST)
7807  * @cfg {String} labelAlign top | left (default top)
7808  * @cfg {String} align left  | right - for navbars
7809  * @cfg {Boolean} loadMask load mask when submit (default true)
7810
7811  *
7812  * @constructor
7813  * Create a new Form
7814  * @param {Object} config The config object
7815  */
7816
7817
7818 Roo.bootstrap.Form = function(config){
7819     
7820     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7821     
7822     Roo.bootstrap.Form.popover.apply();
7823     
7824     this.addEvents({
7825         /**
7826          * @event clientvalidation
7827          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7828          * @param {Form} this
7829          * @param {Boolean} valid true if the form has passed client-side validation
7830          */
7831         clientvalidation: true,
7832         /**
7833          * @event beforeaction
7834          * Fires before any action is performed. Return false to cancel the action.
7835          * @param {Form} this
7836          * @param {Action} action The action to be performed
7837          */
7838         beforeaction: true,
7839         /**
7840          * @event actionfailed
7841          * Fires when an action fails.
7842          * @param {Form} this
7843          * @param {Action} action The action that failed
7844          */
7845         actionfailed : true,
7846         /**
7847          * @event actioncomplete
7848          * Fires when an action is completed.
7849          * @param {Form} this
7850          * @param {Action} action The action that completed
7851          */
7852         actioncomplete : true
7853     });
7854 };
7855
7856 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7857
7858      /**
7859      * @cfg {String} method
7860      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7861      */
7862     method : 'POST',
7863     /**
7864      * @cfg {String} url
7865      * The URL to use for form actions if one isn't supplied in the action options.
7866      */
7867     /**
7868      * @cfg {Boolean} fileUpload
7869      * Set to true if this form is a file upload.
7870      */
7871
7872     /**
7873      * @cfg {Object} baseParams
7874      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7875      */
7876
7877     /**
7878      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7879      */
7880     timeout: 30,
7881     /**
7882      * @cfg {Sting} align (left|right) for navbar forms
7883      */
7884     align : 'left',
7885
7886     // private
7887     activeAction : null,
7888
7889     /**
7890      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7891      * element by passing it or its id or mask the form itself by passing in true.
7892      * @type Mixed
7893      */
7894     waitMsgTarget : false,
7895
7896     loadMask : true,
7897     
7898     /**
7899      * @cfg {Boolean} errorMask (true|false) default false
7900      */
7901     errorMask : false,
7902     
7903     /**
7904      * @cfg {Number} maskOffset Default 100
7905      */
7906     maskOffset : 100,
7907     
7908     /**
7909      * @cfg {Boolean} maskBody
7910      */
7911     maskBody : false,
7912
7913     getAutoCreate : function(){
7914
7915         var cfg = {
7916             tag: 'form',
7917             method : this.method || 'POST',
7918             id : this.id || Roo.id(),
7919             cls : ''
7920         };
7921         if (this.parent().xtype.match(/^Nav/)) {
7922             cfg.cls = 'navbar-form navbar-' + this.align;
7923
7924         }
7925
7926         if (this.labelAlign == 'left' ) {
7927             cfg.cls += ' form-horizontal';
7928         }
7929
7930
7931         return cfg;
7932     },
7933     initEvents : function()
7934     {
7935         this.el.on('submit', this.onSubmit, this);
7936         // this was added as random key presses on the form where triggering form submit.
7937         this.el.on('keypress', function(e) {
7938             if (e.getCharCode() != 13) {
7939                 return true;
7940             }
7941             // we might need to allow it for textareas.. and some other items.
7942             // check e.getTarget().
7943
7944             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7945                 return true;
7946             }
7947
7948             Roo.log("keypress blocked");
7949
7950             e.preventDefault();
7951             return false;
7952         });
7953         
7954     },
7955     // private
7956     onSubmit : function(e){
7957         e.stopEvent();
7958     },
7959
7960      /**
7961      * Returns true if client-side validation on the form is successful.
7962      * @return Boolean
7963      */
7964     isValid : function(){
7965         var items = this.getItems();
7966         var valid = true;
7967         var target = false;
7968         
7969         items.each(function(f){
7970             
7971             if(f.validate()){
7972                 return;
7973             }
7974             
7975             Roo.log('invalid field: ' + f.name);
7976             
7977             valid = false;
7978
7979             if(!target && f.el.isVisible(true)){
7980                 target = f;
7981             }
7982            
7983         });
7984         
7985         if(this.errorMask && !valid){
7986             Roo.bootstrap.Form.popover.mask(this, target);
7987         }
7988         
7989         return valid;
7990     },
7991     
7992     /**
7993      * Returns true if any fields in this form have changed since their original load.
7994      * @return Boolean
7995      */
7996     isDirty : function(){
7997         var dirty = false;
7998         var items = this.getItems();
7999         items.each(function(f){
8000            if(f.isDirty()){
8001                dirty = true;
8002                return false;
8003            }
8004            return true;
8005         });
8006         return dirty;
8007     },
8008      /**
8009      * Performs a predefined action (submit or load) or custom actions you define on this form.
8010      * @param {String} actionName The name of the action type
8011      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8012      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8013      * accept other config options):
8014      * <pre>
8015 Property          Type             Description
8016 ----------------  ---------------  ----------------------------------------------------------------------------------
8017 url               String           The url for the action (defaults to the form's url)
8018 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8019 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8020 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8021                                    validate the form on the client (defaults to false)
8022      * </pre>
8023      * @return {BasicForm} this
8024      */
8025     doAction : function(action, options){
8026         if(typeof action == 'string'){
8027             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8028         }
8029         if(this.fireEvent('beforeaction', this, action) !== false){
8030             this.beforeAction(action);
8031             action.run.defer(100, action);
8032         }
8033         return this;
8034     },
8035
8036     // private
8037     beforeAction : function(action){
8038         var o = action.options;
8039         
8040         if(this.loadMask){
8041             
8042             if(this.maskBody){
8043                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8044             } else {
8045                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8046             }
8047         }
8048         // not really supported yet.. ??
8049
8050         //if(this.waitMsgTarget === true){
8051         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8052         //}else if(this.waitMsgTarget){
8053         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8054         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8055         //}else {
8056         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8057        // }
8058
8059     },
8060
8061     // private
8062     afterAction : function(action, success){
8063         this.activeAction = null;
8064         var o = action.options;
8065
8066         if(this.loadMask){
8067             
8068             if(this.maskBody){
8069                 Roo.get(document.body).unmask();
8070             } else {
8071                 this.el.unmask();
8072             }
8073         }
8074         
8075         //if(this.waitMsgTarget === true){
8076 //            this.el.unmask();
8077         //}else if(this.waitMsgTarget){
8078         //    this.waitMsgTarget.unmask();
8079         //}else{
8080         //    Roo.MessageBox.updateProgress(1);
8081         //    Roo.MessageBox.hide();
8082        // }
8083         //
8084         if(success){
8085             if(o.reset){
8086                 this.reset();
8087             }
8088             Roo.callback(o.success, o.scope, [this, action]);
8089             this.fireEvent('actioncomplete', this, action);
8090
8091         }else{
8092
8093             // failure condition..
8094             // we have a scenario where updates need confirming.
8095             // eg. if a locking scenario exists..
8096             // we look for { errors : { needs_confirm : true }} in the response.
8097             if (
8098                 (typeof(action.result) != 'undefined')  &&
8099                 (typeof(action.result.errors) != 'undefined')  &&
8100                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8101            ){
8102                 var _t = this;
8103                 Roo.log("not supported yet");
8104                  /*
8105
8106                 Roo.MessageBox.confirm(
8107                     "Change requires confirmation",
8108                     action.result.errorMsg,
8109                     function(r) {
8110                         if (r != 'yes') {
8111                             return;
8112                         }
8113                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8114                     }
8115
8116                 );
8117                 */
8118
8119
8120                 return;
8121             }
8122
8123             Roo.callback(o.failure, o.scope, [this, action]);
8124             // show an error message if no failed handler is set..
8125             if (!this.hasListener('actionfailed')) {
8126                 Roo.log("need to add dialog support");
8127                 /*
8128                 Roo.MessageBox.alert("Error",
8129                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8130                         action.result.errorMsg :
8131                         "Saving Failed, please check your entries or try again"
8132                 );
8133                 */
8134             }
8135
8136             this.fireEvent('actionfailed', this, action);
8137         }
8138
8139     },
8140     /**
8141      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8142      * @param {String} id The value to search for
8143      * @return Field
8144      */
8145     findField : function(id){
8146         var items = this.getItems();
8147         var field = items.get(id);
8148         if(!field){
8149              items.each(function(f){
8150                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8151                     field = f;
8152                     return false;
8153                 }
8154                 return true;
8155             });
8156         }
8157         return field || null;
8158     },
8159      /**
8160      * Mark fields in this form invalid in bulk.
8161      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8162      * @return {BasicForm} this
8163      */
8164     markInvalid : function(errors){
8165         if(errors instanceof Array){
8166             for(var i = 0, len = errors.length; i < len; i++){
8167                 var fieldError = errors[i];
8168                 var f = this.findField(fieldError.id);
8169                 if(f){
8170                     f.markInvalid(fieldError.msg);
8171                 }
8172             }
8173         }else{
8174             var field, id;
8175             for(id in errors){
8176                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8177                     field.markInvalid(errors[id]);
8178                 }
8179             }
8180         }
8181         //Roo.each(this.childForms || [], function (f) {
8182         //    f.markInvalid(errors);
8183         //});
8184
8185         return this;
8186     },
8187
8188     /**
8189      * Set values for fields in this form in bulk.
8190      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8191      * @return {BasicForm} this
8192      */
8193     setValues : function(values){
8194         if(values instanceof Array){ // array of objects
8195             for(var i = 0, len = values.length; i < len; i++){
8196                 var v = values[i];
8197                 var f = this.findField(v.id);
8198                 if(f){
8199                     f.setValue(v.value);
8200                     if(this.trackResetOnLoad){
8201                         f.originalValue = f.getValue();
8202                     }
8203                 }
8204             }
8205         }else{ // object hash
8206             var field, id;
8207             for(id in values){
8208                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8209
8210                     if (field.setFromData &&
8211                         field.valueField &&
8212                         field.displayField &&
8213                         // combos' with local stores can
8214                         // be queried via setValue()
8215                         // to set their value..
8216                         (field.store && !field.store.isLocal)
8217                         ) {
8218                         // it's a combo
8219                         var sd = { };
8220                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8221                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8222                         field.setFromData(sd);
8223
8224                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8225                         
8226                         field.setFromData(values);
8227                         
8228                     } else {
8229                         field.setValue(values[id]);
8230                     }
8231
8232
8233                     if(this.trackResetOnLoad){
8234                         field.originalValue = field.getValue();
8235                     }
8236                 }
8237             }
8238         }
8239
8240         //Roo.each(this.childForms || [], function (f) {
8241         //    f.setValues(values);
8242         //});
8243
8244         return this;
8245     },
8246
8247     /**
8248      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8249      * they are returned as an array.
8250      * @param {Boolean} asString
8251      * @return {Object}
8252      */
8253     getValues : function(asString){
8254         //if (this.childForms) {
8255             // copy values from the child forms
8256         //    Roo.each(this.childForms, function (f) {
8257         //        this.setValues(f.getValues());
8258         //    }, this);
8259         //}
8260
8261
8262
8263         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8264         if(asString === true){
8265             return fs;
8266         }
8267         return Roo.urlDecode(fs);
8268     },
8269
8270     /**
8271      * Returns the fields in this form as an object with key/value pairs.
8272      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8273      * @return {Object}
8274      */
8275     getFieldValues : function(with_hidden)
8276     {
8277         var items = this.getItems();
8278         var ret = {};
8279         items.each(function(f){
8280             
8281             if (!f.getName()) {
8282                 return;
8283             }
8284             
8285             var v = f.getValue();
8286             
8287             if (f.inputType =='radio') {
8288                 if (typeof(ret[f.getName()]) == 'undefined') {
8289                     ret[f.getName()] = ''; // empty..
8290                 }
8291
8292                 if (!f.el.dom.checked) {
8293                     return;
8294
8295                 }
8296                 v = f.el.dom.value;
8297
8298             }
8299             
8300             if(f.xtype == 'MoneyField'){
8301                 ret[f.currencyName] = f.getCurrency();
8302             }
8303
8304             // not sure if this supported any more..
8305             if ((typeof(v) == 'object') && f.getRawValue) {
8306                 v = f.getRawValue() ; // dates..
8307             }
8308             // combo boxes where name != hiddenName...
8309             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8310                 ret[f.name] = f.getRawValue();
8311             }
8312             ret[f.getName()] = v;
8313         });
8314
8315         return ret;
8316     },
8317
8318     /**
8319      * Clears all invalid messages in this form.
8320      * @return {BasicForm} this
8321      */
8322     clearInvalid : function(){
8323         var items = this.getItems();
8324
8325         items.each(function(f){
8326            f.clearInvalid();
8327         });
8328
8329         return this;
8330     },
8331
8332     /**
8333      * Resets this form.
8334      * @return {BasicForm} this
8335      */
8336     reset : function(){
8337         var items = this.getItems();
8338         items.each(function(f){
8339             f.reset();
8340         });
8341
8342         Roo.each(this.childForms || [], function (f) {
8343             f.reset();
8344         });
8345
8346
8347         return this;
8348     },
8349     
8350     getItems : function()
8351     {
8352         var r=new Roo.util.MixedCollection(false, function(o){
8353             return o.id || (o.id = Roo.id());
8354         });
8355         var iter = function(el) {
8356             if (el.inputEl) {
8357                 r.add(el);
8358             }
8359             if (!el.items) {
8360                 return;
8361             }
8362             Roo.each(el.items,function(e) {
8363                 iter(e);
8364             });
8365         };
8366
8367         iter(this);
8368         return r;
8369     },
8370     
8371     hideFields : function(items)
8372     {
8373         Roo.each(items, function(i){
8374             
8375             var f = this.findField(i);
8376             
8377             if(!f){
8378                 return;
8379             }
8380             
8381             f.hide();
8382             
8383         }, this);
8384     },
8385     
8386     showFields : function(items)
8387     {
8388         Roo.each(items, function(i){
8389             
8390             var f = this.findField(i);
8391             
8392             if(!f){
8393                 return;
8394             }
8395             
8396             f.show();
8397             
8398         }, this);
8399     }
8400
8401 });
8402
8403 Roo.apply(Roo.bootstrap.Form, {
8404     
8405     popover : {
8406         
8407         padding : 5,
8408         
8409         isApplied : false,
8410         
8411         isMasked : false,
8412         
8413         form : false,
8414         
8415         target : false,
8416         
8417         toolTip : false,
8418         
8419         intervalID : false,
8420         
8421         maskEl : false,
8422         
8423         apply : function()
8424         {
8425             if(this.isApplied){
8426                 return;
8427             }
8428             
8429             this.maskEl = {
8430                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8431                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8432                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8433                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8434             };
8435             
8436             this.maskEl.top.enableDisplayMode("block");
8437             this.maskEl.left.enableDisplayMode("block");
8438             this.maskEl.bottom.enableDisplayMode("block");
8439             this.maskEl.right.enableDisplayMode("block");
8440             
8441             this.toolTip = new Roo.bootstrap.Tooltip({
8442                 cls : 'roo-form-error-popover',
8443                 alignment : {
8444                     'left' : ['r-l', [-2,0], 'right'],
8445                     'right' : ['l-r', [2,0], 'left'],
8446                     'bottom' : ['tl-bl', [0,2], 'top'],
8447                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8448                 }
8449             });
8450             
8451             this.toolTip.render(Roo.get(document.body));
8452
8453             this.toolTip.el.enableDisplayMode("block");
8454             
8455             Roo.get(document.body).on('click', function(){
8456                 this.unmask();
8457             }, this);
8458             
8459             Roo.get(document.body).on('touchstart', function(){
8460                 this.unmask();
8461             }, this);
8462             
8463             this.isApplied = true
8464         },
8465         
8466         mask : function(form, target)
8467         {
8468             this.form = form;
8469             
8470             this.target = target;
8471             
8472             if(!this.form.errorMask || !target.el){
8473                 return;
8474             }
8475             
8476             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8477             
8478             Roo.log(scrollable);
8479             
8480             var ot = this.target.el.calcOffsetsTo(scrollable);
8481             
8482             var scrollTo = ot[1] - this.form.maskOffset;
8483             
8484             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8485             
8486             scrollable.scrollTo('top', scrollTo);
8487             
8488             var box = this.target.el.getBox();
8489             Roo.log(box);
8490             var zIndex = Roo.bootstrap.Modal.zIndex++;
8491
8492             
8493             this.maskEl.top.setStyle('position', 'absolute');
8494             this.maskEl.top.setStyle('z-index', zIndex);
8495             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8496             this.maskEl.top.setLeft(0);
8497             this.maskEl.top.setTop(0);
8498             this.maskEl.top.show();
8499             
8500             this.maskEl.left.setStyle('position', 'absolute');
8501             this.maskEl.left.setStyle('z-index', zIndex);
8502             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8503             this.maskEl.left.setLeft(0);
8504             this.maskEl.left.setTop(box.y - this.padding);
8505             this.maskEl.left.show();
8506
8507             this.maskEl.bottom.setStyle('position', 'absolute');
8508             this.maskEl.bottom.setStyle('z-index', zIndex);
8509             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8510             this.maskEl.bottom.setLeft(0);
8511             this.maskEl.bottom.setTop(box.bottom + this.padding);
8512             this.maskEl.bottom.show();
8513
8514             this.maskEl.right.setStyle('position', 'absolute');
8515             this.maskEl.right.setStyle('z-index', zIndex);
8516             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8517             this.maskEl.right.setLeft(box.right + this.padding);
8518             this.maskEl.right.setTop(box.y - this.padding);
8519             this.maskEl.right.show();
8520
8521             this.toolTip.bindEl = this.target.el;
8522
8523             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8524
8525             var tip = this.target.blankText;
8526
8527             if(this.target.getValue() !== '' ) {
8528                 
8529                 if (this.target.invalidText.length) {
8530                     tip = this.target.invalidText;
8531                 } else if (this.target.regexText.length){
8532                     tip = this.target.regexText;
8533                 }
8534             }
8535
8536             this.toolTip.show(tip);
8537
8538             this.intervalID = window.setInterval(function() {
8539                 Roo.bootstrap.Form.popover.unmask();
8540             }, 10000);
8541
8542             window.onwheel = function(){ return false;};
8543             
8544             (function(){ this.isMasked = true; }).defer(500, this);
8545             
8546         },
8547         
8548         unmask : function()
8549         {
8550             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8551                 return;
8552             }
8553             
8554             this.maskEl.top.setStyle('position', 'absolute');
8555             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8556             this.maskEl.top.hide();
8557
8558             this.maskEl.left.setStyle('position', 'absolute');
8559             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8560             this.maskEl.left.hide();
8561
8562             this.maskEl.bottom.setStyle('position', 'absolute');
8563             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8564             this.maskEl.bottom.hide();
8565
8566             this.maskEl.right.setStyle('position', 'absolute');
8567             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8568             this.maskEl.right.hide();
8569             
8570             this.toolTip.hide();
8571             
8572             this.toolTip.el.hide();
8573             
8574             window.onwheel = function(){ return true;};
8575             
8576             if(this.intervalID){
8577                 window.clearInterval(this.intervalID);
8578                 this.intervalID = false;
8579             }
8580             
8581             this.isMasked = false;
8582             
8583         }
8584         
8585     }
8586     
8587 });
8588
8589 /*
8590  * Based on:
8591  * Ext JS Library 1.1.1
8592  * Copyright(c) 2006-2007, Ext JS, LLC.
8593  *
8594  * Originally Released Under LGPL - original licence link has changed is not relivant.
8595  *
8596  * Fork - LGPL
8597  * <script type="text/javascript">
8598  */
8599 /**
8600  * @class Roo.form.VTypes
8601  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8602  * @singleton
8603  */
8604 Roo.form.VTypes = function(){
8605     // closure these in so they are only created once.
8606     var alpha = /^[a-zA-Z_]+$/;
8607     var alphanum = /^[a-zA-Z0-9_]+$/;
8608     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8609     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8610
8611     // All these messages and functions are configurable
8612     return {
8613         /**
8614          * The function used to validate email addresses
8615          * @param {String} value The email address
8616          */
8617         'email' : function(v){
8618             return email.test(v);
8619         },
8620         /**
8621          * The error text to display when the email validation function returns false
8622          * @type String
8623          */
8624         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8625         /**
8626          * The keystroke filter mask to be applied on email input
8627          * @type RegExp
8628          */
8629         'emailMask' : /[a-z0-9_\.\-@]/i,
8630
8631         /**
8632          * The function used to validate URLs
8633          * @param {String} value The URL
8634          */
8635         'url' : function(v){
8636             return url.test(v);
8637         },
8638         /**
8639          * The error text to display when the url validation function returns false
8640          * @type String
8641          */
8642         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8643         
8644         /**
8645          * The function used to validate alpha values
8646          * @param {String} value The value
8647          */
8648         'alpha' : function(v){
8649             return alpha.test(v);
8650         },
8651         /**
8652          * The error text to display when the alpha validation function returns false
8653          * @type String
8654          */
8655         'alphaText' : 'This field should only contain letters and _',
8656         /**
8657          * The keystroke filter mask to be applied on alpha input
8658          * @type RegExp
8659          */
8660         'alphaMask' : /[a-z_]/i,
8661
8662         /**
8663          * The function used to validate alphanumeric values
8664          * @param {String} value The value
8665          */
8666         'alphanum' : function(v){
8667             return alphanum.test(v);
8668         },
8669         /**
8670          * The error text to display when the alphanumeric validation function returns false
8671          * @type String
8672          */
8673         'alphanumText' : 'This field should only contain letters, numbers and _',
8674         /**
8675          * The keystroke filter mask to be applied on alphanumeric input
8676          * @type RegExp
8677          */
8678         'alphanumMask' : /[a-z0-9_]/i
8679     };
8680 }();/*
8681  * - LGPL
8682  *
8683  * Input
8684  * 
8685  */
8686
8687 /**
8688  * @class Roo.bootstrap.Input
8689  * @extends Roo.bootstrap.Component
8690  * Bootstrap Input class
8691  * @cfg {Boolean} disabled is it disabled
8692  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8693  * @cfg {String} name name of the input
8694  * @cfg {string} fieldLabel - the label associated
8695  * @cfg {string} placeholder - placeholder to put in text.
8696  * @cfg {string}  before - input group add on before
8697  * @cfg {string} after - input group add on after
8698  * @cfg {string} size - (lg|sm) or leave empty..
8699  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8700  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8701  * @cfg {Number} md colspan out of 12 for computer-sized screens
8702  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8703  * @cfg {string} value default value of the input
8704  * @cfg {Number} labelWidth set the width of label 
8705  * @cfg {Number} labellg set the width of label (1-12)
8706  * @cfg {Number} labelmd set the width of label (1-12)
8707  * @cfg {Number} labelsm set the width of label (1-12)
8708  * @cfg {Number} labelxs set the width of label (1-12)
8709  * @cfg {String} labelAlign (top|left)
8710  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8711  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8712  * @cfg {String} indicatorpos (left|right) default left
8713  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8714  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8715
8716  * @cfg {String} align (left|center|right) Default left
8717  * @cfg {Boolean} forceFeedback (true|false) Default false
8718  * 
8719  * @constructor
8720  * Create a new Input
8721  * @param {Object} config The config object
8722  */
8723
8724 Roo.bootstrap.Input = function(config){
8725     
8726     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8727     
8728     this.addEvents({
8729         /**
8730          * @event focus
8731          * Fires when this field receives input focus.
8732          * @param {Roo.form.Field} this
8733          */
8734         focus : true,
8735         /**
8736          * @event blur
8737          * Fires when this field loses input focus.
8738          * @param {Roo.form.Field} this
8739          */
8740         blur : true,
8741         /**
8742          * @event specialkey
8743          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8744          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8745          * @param {Roo.form.Field} this
8746          * @param {Roo.EventObject} e The event object
8747          */
8748         specialkey : true,
8749         /**
8750          * @event change
8751          * Fires just before the field blurs if the field value has changed.
8752          * @param {Roo.form.Field} this
8753          * @param {Mixed} newValue The new value
8754          * @param {Mixed} oldValue The original value
8755          */
8756         change : true,
8757         /**
8758          * @event invalid
8759          * Fires after the field has been marked as invalid.
8760          * @param {Roo.form.Field} this
8761          * @param {String} msg The validation message
8762          */
8763         invalid : true,
8764         /**
8765          * @event valid
8766          * Fires after the field has been validated with no errors.
8767          * @param {Roo.form.Field} this
8768          */
8769         valid : true,
8770          /**
8771          * @event keyup
8772          * Fires after the key up
8773          * @param {Roo.form.Field} this
8774          * @param {Roo.EventObject}  e The event Object
8775          */
8776         keyup : true
8777     });
8778 };
8779
8780 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8781      /**
8782      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8783       automatic validation (defaults to "keyup").
8784      */
8785     validationEvent : "keyup",
8786      /**
8787      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8788      */
8789     validateOnBlur : true,
8790     /**
8791      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8792      */
8793     validationDelay : 250,
8794      /**
8795      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8796      */
8797     focusClass : "x-form-focus",  // not needed???
8798     
8799        
8800     /**
8801      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8802      */
8803     invalidClass : "has-warning",
8804     
8805     /**
8806      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8807      */
8808     validClass : "has-success",
8809     
8810     /**
8811      * @cfg {Boolean} hasFeedback (true|false) default true
8812      */
8813     hasFeedback : true,
8814     
8815     /**
8816      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8817      */
8818     invalidFeedbackClass : "glyphicon-warning-sign",
8819     
8820     /**
8821      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8822      */
8823     validFeedbackClass : "glyphicon-ok",
8824     
8825     /**
8826      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8827      */
8828     selectOnFocus : false,
8829     
8830      /**
8831      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8832      */
8833     maskRe : null,
8834        /**
8835      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8836      */
8837     vtype : null,
8838     
8839       /**
8840      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8841      */
8842     disableKeyFilter : false,
8843     
8844        /**
8845      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8846      */
8847     disabled : false,
8848      /**
8849      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8850      */
8851     allowBlank : true,
8852     /**
8853      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8854      */
8855     blankText : "Please complete this mandatory field",
8856     
8857      /**
8858      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8859      */
8860     minLength : 0,
8861     /**
8862      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8863      */
8864     maxLength : Number.MAX_VALUE,
8865     /**
8866      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8867      */
8868     minLengthText : "The minimum length for this field is {0}",
8869     /**
8870      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8871      */
8872     maxLengthText : "The maximum length for this field is {0}",
8873   
8874     
8875     /**
8876      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8877      * If available, this function will be called only after the basic validators all return true, and will be passed the
8878      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8879      */
8880     validator : null,
8881     /**
8882      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8883      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8884      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8885      */
8886     regex : null,
8887     /**
8888      * @cfg {String} regexText -- Depricated - use Invalid Text
8889      */
8890     regexText : "",
8891     
8892     /**
8893      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8894      */
8895     invalidText : "",
8896     
8897     
8898     
8899     autocomplete: false,
8900     
8901     
8902     fieldLabel : '',
8903     inputType : 'text',
8904     
8905     name : false,
8906     placeholder: false,
8907     before : false,
8908     after : false,
8909     size : false,
8910     hasFocus : false,
8911     preventMark: false,
8912     isFormField : true,
8913     value : '',
8914     labelWidth : 2,
8915     labelAlign : false,
8916     readOnly : false,
8917     align : false,
8918     formatedValue : false,
8919     forceFeedback : false,
8920     
8921     indicatorpos : 'left',
8922     
8923     labellg : 0,
8924     labelmd : 0,
8925     labelsm : 0,
8926     labelxs : 0,
8927     
8928     capture : '',
8929     accept : '',
8930     
8931     parentLabelAlign : function()
8932     {
8933         var parent = this;
8934         while (parent.parent()) {
8935             parent = parent.parent();
8936             if (typeof(parent.labelAlign) !='undefined') {
8937                 return parent.labelAlign;
8938             }
8939         }
8940         return 'left';
8941         
8942     },
8943     
8944     getAutoCreate : function()
8945     {
8946         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8947         
8948         var id = Roo.id();
8949         
8950         var cfg = {};
8951         
8952         if(this.inputType != 'hidden'){
8953             cfg.cls = 'form-group' //input-group
8954         }
8955         
8956         var input =  {
8957             tag: 'input',
8958             id : id,
8959             type : this.inputType,
8960             value : this.value,
8961             cls : 'form-control',
8962             placeholder : this.placeholder || '',
8963             autocomplete : this.autocomplete || 'new-password'
8964         };
8965         
8966         if(this.capture.length){
8967             input.capture = this.capture;
8968         }
8969         
8970         if(this.accept.length){
8971             input.accept = this.accept + "/*";
8972         }
8973         
8974         if(this.align){
8975             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8976         }
8977         
8978         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8979             input.maxLength = this.maxLength;
8980         }
8981         
8982         if (this.disabled) {
8983             input.disabled=true;
8984         }
8985         
8986         if (this.readOnly) {
8987             input.readonly=true;
8988         }
8989         
8990         if (this.name) {
8991             input.name = this.name;
8992         }
8993         
8994         if (this.size) {
8995             input.cls += ' input-' + this.size;
8996         }
8997         
8998         var settings=this;
8999         ['xs','sm','md','lg'].map(function(size){
9000             if (settings[size]) {
9001                 cfg.cls += ' col-' + size + '-' + settings[size];
9002             }
9003         });
9004         
9005         var inputblock = input;
9006         
9007         var feedback = {
9008             tag: 'span',
9009             cls: 'glyphicon form-control-feedback'
9010         };
9011             
9012         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9013             
9014             inputblock = {
9015                 cls : 'has-feedback',
9016                 cn :  [
9017                     input,
9018                     feedback
9019                 ] 
9020             };  
9021         }
9022         
9023         if (this.before || this.after) {
9024             
9025             inputblock = {
9026                 cls : 'input-group',
9027                 cn :  [] 
9028             };
9029             
9030             if (this.before && typeof(this.before) == 'string') {
9031                 
9032                 inputblock.cn.push({
9033                     tag :'span',
9034                     cls : 'roo-input-before input-group-addon',
9035                     html : this.before
9036                 });
9037             }
9038             if (this.before && typeof(this.before) == 'object') {
9039                 this.before = Roo.factory(this.before);
9040                 
9041                 inputblock.cn.push({
9042                     tag :'span',
9043                     cls : 'roo-input-before input-group-' +
9044                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9045                 });
9046             }
9047             
9048             inputblock.cn.push(input);
9049             
9050             if (this.after && typeof(this.after) == 'string') {
9051                 inputblock.cn.push({
9052                     tag :'span',
9053                     cls : 'roo-input-after input-group-addon',
9054                     html : this.after
9055                 });
9056             }
9057             if (this.after && typeof(this.after) == 'object') {
9058                 this.after = Roo.factory(this.after);
9059                 
9060                 inputblock.cn.push({
9061                     tag :'span',
9062                     cls : 'roo-input-after input-group-' +
9063                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9064                 });
9065             }
9066             
9067             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9068                 inputblock.cls += ' has-feedback';
9069                 inputblock.cn.push(feedback);
9070             }
9071         };
9072         
9073         if (align ==='left' && this.fieldLabel.length) {
9074             
9075             cfg.cls += ' roo-form-group-label-left';
9076             
9077             cfg.cn = [
9078                 {
9079                     tag : 'i',
9080                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9081                     tooltip : 'This field is required'
9082                 },
9083                 {
9084                     tag: 'label',
9085                     'for' :  id,
9086                     cls : 'control-label',
9087                     html : this.fieldLabel
9088
9089                 },
9090                 {
9091                     cls : "", 
9092                     cn: [
9093                         inputblock
9094                     ]
9095                 }
9096             ];
9097             
9098             var labelCfg = cfg.cn[1];
9099             var contentCfg = cfg.cn[2];
9100             
9101             if(this.indicatorpos == 'right'){
9102                 cfg.cn = [
9103                     {
9104                         tag: 'label',
9105                         'for' :  id,
9106                         cls : 'control-label',
9107                         cn : [
9108                             {
9109                                 tag : 'span',
9110                                 html : this.fieldLabel
9111                             },
9112                             {
9113                                 tag : 'i',
9114                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9115                                 tooltip : 'This field is required'
9116                             }
9117                         ]
9118                     },
9119                     {
9120                         cls : "",
9121                         cn: [
9122                             inputblock
9123                         ]
9124                     }
9125
9126                 ];
9127                 
9128                 labelCfg = cfg.cn[0];
9129                 contentCfg = cfg.cn[1];
9130             
9131             }
9132             
9133             if(this.labelWidth > 12){
9134                 labelCfg.style = "width: " + this.labelWidth + 'px';
9135             }
9136             
9137             if(this.labelWidth < 13 && this.labelmd == 0){
9138                 this.labelmd = this.labelWidth;
9139             }
9140             
9141             if(this.labellg > 0){
9142                 labelCfg.cls += ' col-lg-' + this.labellg;
9143                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9144             }
9145             
9146             if(this.labelmd > 0){
9147                 labelCfg.cls += ' col-md-' + this.labelmd;
9148                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9149             }
9150             
9151             if(this.labelsm > 0){
9152                 labelCfg.cls += ' col-sm-' + this.labelsm;
9153                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9154             }
9155             
9156             if(this.labelxs > 0){
9157                 labelCfg.cls += ' col-xs-' + this.labelxs;
9158                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9159             }
9160             
9161             
9162         } else if ( this.fieldLabel.length) {
9163                 
9164             cfg.cn = [
9165                 {
9166                     tag : 'i',
9167                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9168                     tooltip : 'This field is required'
9169                 },
9170                 {
9171                     tag: 'label',
9172                    //cls : 'input-group-addon',
9173                     html : this.fieldLabel
9174
9175                 },
9176
9177                inputblock
9178
9179            ];
9180            
9181            if(this.indicatorpos == 'right'){
9182                 
9183                 cfg.cn = [
9184                     {
9185                         tag: 'label',
9186                        //cls : 'input-group-addon',
9187                         html : this.fieldLabel
9188
9189                     },
9190                     {
9191                         tag : 'i',
9192                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9193                         tooltip : 'This field is required'
9194                     },
9195
9196                    inputblock
9197
9198                ];
9199
9200             }
9201
9202         } else {
9203             
9204             cfg.cn = [
9205
9206                     inputblock
9207
9208             ];
9209                 
9210                 
9211         };
9212         
9213         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9214            cfg.cls += ' navbar-form';
9215         }
9216         
9217         if (this.parentType === 'NavGroup') {
9218            cfg.cls += ' navbar-form';
9219            cfg.tag = 'li';
9220         }
9221         
9222         return cfg;
9223         
9224     },
9225     /**
9226      * return the real input element.
9227      */
9228     inputEl: function ()
9229     {
9230         return this.el.select('input.form-control',true).first();
9231     },
9232     
9233     tooltipEl : function()
9234     {
9235         return this.inputEl();
9236     },
9237     
9238     indicatorEl : function()
9239     {
9240         var indicator = this.el.select('i.roo-required-indicator',true).first();
9241         
9242         if(!indicator){
9243             return false;
9244         }
9245         
9246         return indicator;
9247         
9248     },
9249     
9250     setDisabled : function(v)
9251     {
9252         var i  = this.inputEl().dom;
9253         if (!v) {
9254             i.removeAttribute('disabled');
9255             return;
9256             
9257         }
9258         i.setAttribute('disabled','true');
9259     },
9260     initEvents : function()
9261     {
9262           
9263         this.inputEl().on("keydown" , this.fireKey,  this);
9264         this.inputEl().on("focus", this.onFocus,  this);
9265         this.inputEl().on("blur", this.onBlur,  this);
9266         
9267         this.inputEl().relayEvent('keyup', this);
9268         
9269         this.indicator = this.indicatorEl();
9270         
9271         if(this.indicator){
9272             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9273         }
9274  
9275         // reference to original value for reset
9276         this.originalValue = this.getValue();
9277         //Roo.form.TextField.superclass.initEvents.call(this);
9278         if(this.validationEvent == 'keyup'){
9279             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9280             this.inputEl().on('keyup', this.filterValidation, this);
9281         }
9282         else if(this.validationEvent !== false){
9283             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9284         }
9285         
9286         if(this.selectOnFocus){
9287             this.on("focus", this.preFocus, this);
9288             
9289         }
9290         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9291             this.inputEl().on("keypress", this.filterKeys, this);
9292         } else {
9293             this.inputEl().relayEvent('keypress', this);
9294         }
9295        /* if(this.grow){
9296             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9297             this.el.on("click", this.autoSize,  this);
9298         }
9299         */
9300         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9301             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9302         }
9303         
9304         if (typeof(this.before) == 'object') {
9305             this.before.render(this.el.select('.roo-input-before',true).first());
9306         }
9307         if (typeof(this.after) == 'object') {
9308             this.after.render(this.el.select('.roo-input-after',true).first());
9309         }
9310         
9311         this.inputEl().on('change', this.onChange, this);
9312         
9313     },
9314     filterValidation : function(e){
9315         if(!e.isNavKeyPress()){
9316             this.validationTask.delay(this.validationDelay);
9317         }
9318     },
9319      /**
9320      * Validates the field value
9321      * @return {Boolean} True if the value is valid, else false
9322      */
9323     validate : function(){
9324         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9325         if(this.disabled || this.validateValue(this.getRawValue())){
9326             this.markValid();
9327             return true;
9328         }
9329         
9330         this.markInvalid();
9331         return false;
9332     },
9333     
9334     
9335     /**
9336      * Validates a value according to the field's validation rules and marks the field as invalid
9337      * if the validation fails
9338      * @param {Mixed} value The value to validate
9339      * @return {Boolean} True if the value is valid, else false
9340      */
9341     validateValue : function(value)
9342     {
9343         if(this.getVisibilityEl().hasClass('hidden')){
9344             return true;
9345         }
9346         
9347         if(value.length < 1)  { // if it's blank
9348             if(this.allowBlank){
9349                 return true;
9350             }
9351             return false;
9352         }
9353         
9354         if(value.length < this.minLength){
9355             return false;
9356         }
9357         if(value.length > this.maxLength){
9358             return false;
9359         }
9360         if(this.vtype){
9361             var vt = Roo.form.VTypes;
9362             if(!vt[this.vtype](value, this)){
9363                 return false;
9364             }
9365         }
9366         if(typeof this.validator == "function"){
9367             var msg = this.validator(value);
9368             if(msg !== true){
9369                 return false;
9370             }
9371             if (typeof(msg) == 'string') {
9372                 this.invalidText = msg;
9373             }
9374         }
9375         
9376         if(this.regex && !this.regex.test(value)){
9377             return false;
9378         }
9379         
9380         return true;
9381     },
9382     
9383      // private
9384     fireKey : function(e){
9385         //Roo.log('field ' + e.getKey());
9386         if(e.isNavKeyPress()){
9387             this.fireEvent("specialkey", this, e);
9388         }
9389     },
9390     focus : function (selectText){
9391         if(this.rendered){
9392             this.inputEl().focus();
9393             if(selectText === true){
9394                 this.inputEl().dom.select();
9395             }
9396         }
9397         return this;
9398     } ,
9399     
9400     onFocus : function(){
9401         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9402            // this.el.addClass(this.focusClass);
9403         }
9404         if(!this.hasFocus){
9405             this.hasFocus = true;
9406             this.startValue = this.getValue();
9407             this.fireEvent("focus", this);
9408         }
9409     },
9410     
9411     beforeBlur : Roo.emptyFn,
9412
9413     
9414     // private
9415     onBlur : function(){
9416         this.beforeBlur();
9417         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9418             //this.el.removeClass(this.focusClass);
9419         }
9420         this.hasFocus = false;
9421         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9422             this.validate();
9423         }
9424         var v = this.getValue();
9425         if(String(v) !== String(this.startValue)){
9426             this.fireEvent('change', this, v, this.startValue);
9427         }
9428         this.fireEvent("blur", this);
9429     },
9430     
9431     onChange : function(e)
9432     {
9433         var v = this.getValue();
9434         if(String(v) !== String(this.startValue)){
9435             this.fireEvent('change', this, v, this.startValue);
9436         }
9437         
9438     },
9439     
9440     /**
9441      * Resets the current field value to the originally loaded value and clears any validation messages
9442      */
9443     reset : function(){
9444         this.setValue(this.originalValue);
9445         this.validate();
9446     },
9447      /**
9448      * Returns the name of the field
9449      * @return {Mixed} name The name field
9450      */
9451     getName: function(){
9452         return this.name;
9453     },
9454      /**
9455      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9456      * @return {Mixed} value The field value
9457      */
9458     getValue : function(){
9459         
9460         var v = this.inputEl().getValue();
9461         
9462         return v;
9463     },
9464     /**
9465      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9466      * @return {Mixed} value The field value
9467      */
9468     getRawValue : function(){
9469         var v = this.inputEl().getValue();
9470         
9471         return v;
9472     },
9473     
9474     /**
9475      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9476      * @param {Mixed} value The value to set
9477      */
9478     setRawValue : function(v){
9479         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9480     },
9481     
9482     selectText : function(start, end){
9483         var v = this.getRawValue();
9484         if(v.length > 0){
9485             start = start === undefined ? 0 : start;
9486             end = end === undefined ? v.length : end;
9487             var d = this.inputEl().dom;
9488             if(d.setSelectionRange){
9489                 d.setSelectionRange(start, end);
9490             }else if(d.createTextRange){
9491                 var range = d.createTextRange();
9492                 range.moveStart("character", start);
9493                 range.moveEnd("character", v.length-end);
9494                 range.select();
9495             }
9496         }
9497     },
9498     
9499     /**
9500      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9501      * @param {Mixed} value The value to set
9502      */
9503     setValue : function(v){
9504         this.value = v;
9505         if(this.rendered){
9506             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9507             this.validate();
9508         }
9509     },
9510     
9511     /*
9512     processValue : function(value){
9513         if(this.stripCharsRe){
9514             var newValue = value.replace(this.stripCharsRe, '');
9515             if(newValue !== value){
9516                 this.setRawValue(newValue);
9517                 return newValue;
9518             }
9519         }
9520         return value;
9521     },
9522   */
9523     preFocus : function(){
9524         
9525         if(this.selectOnFocus){
9526             this.inputEl().dom.select();
9527         }
9528     },
9529     filterKeys : function(e){
9530         var k = e.getKey();
9531         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9532             return;
9533         }
9534         var c = e.getCharCode(), cc = String.fromCharCode(c);
9535         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9536             return;
9537         }
9538         if(!this.maskRe.test(cc)){
9539             e.stopEvent();
9540         }
9541     },
9542      /**
9543      * Clear any invalid styles/messages for this field
9544      */
9545     clearInvalid : function(){
9546         
9547         if(!this.el || this.preventMark){ // not rendered
9548             return;
9549         }
9550         
9551      
9552         this.el.removeClass(this.invalidClass);
9553         
9554         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9555             
9556             var feedback = this.el.select('.form-control-feedback', true).first();
9557             
9558             if(feedback){
9559                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9560             }
9561             
9562         }
9563         
9564         if(this.indicator){
9565             this.indicator.removeClass('visible');
9566             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9567         }
9568         
9569         this.fireEvent('valid', this);
9570     },
9571     
9572      /**
9573      * Mark this field as valid
9574      */
9575     markValid : function()
9576     {
9577         if(!this.el  || this.preventMark){ // not rendered...
9578             return;
9579         }
9580         
9581         this.el.removeClass([this.invalidClass, this.validClass]);
9582         
9583         var feedback = this.el.select('.form-control-feedback', true).first();
9584             
9585         if(feedback){
9586             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9587         }
9588         
9589         if(this.indicator){
9590             this.indicator.removeClass('visible');
9591             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9592         }
9593         
9594         if(this.disabled){
9595             return;
9596         }
9597         
9598         if(this.allowBlank && !this.getRawValue().length){
9599             return;
9600         }
9601         
9602         this.el.addClass(this.validClass);
9603         
9604         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9605             
9606             var feedback = this.el.select('.form-control-feedback', true).first();
9607             
9608             if(feedback){
9609                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9610                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9611             }
9612             
9613         }
9614         
9615         this.fireEvent('valid', this);
9616     },
9617     
9618      /**
9619      * Mark this field as invalid
9620      * @param {String} msg The validation message
9621      */
9622     markInvalid : function(msg)
9623     {
9624         if(!this.el  || this.preventMark){ // not rendered
9625             return;
9626         }
9627         
9628         this.el.removeClass([this.invalidClass, this.validClass]);
9629         
9630         var feedback = this.el.select('.form-control-feedback', true).first();
9631             
9632         if(feedback){
9633             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9634         }
9635
9636         if(this.disabled){
9637             return;
9638         }
9639         
9640         if(this.allowBlank && !this.getRawValue().length){
9641             return;
9642         }
9643         
9644         if(this.indicator){
9645             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9646             this.indicator.addClass('visible');
9647         }
9648         
9649         this.el.addClass(this.invalidClass);
9650         
9651         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9652             
9653             var feedback = this.el.select('.form-control-feedback', true).first();
9654             
9655             if(feedback){
9656                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9657                 
9658                 if(this.getValue().length || this.forceFeedback){
9659                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9660                 }
9661                 
9662             }
9663             
9664         }
9665         
9666         this.fireEvent('invalid', this, msg);
9667     },
9668     // private
9669     SafariOnKeyDown : function(event)
9670     {
9671         // this is a workaround for a password hang bug on chrome/ webkit.
9672         if (this.inputEl().dom.type != 'password') {
9673             return;
9674         }
9675         
9676         var isSelectAll = false;
9677         
9678         if(this.inputEl().dom.selectionEnd > 0){
9679             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9680         }
9681         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9682             event.preventDefault();
9683             this.setValue('');
9684             return;
9685         }
9686         
9687         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9688             
9689             event.preventDefault();
9690             // this is very hacky as keydown always get's upper case.
9691             //
9692             var cc = String.fromCharCode(event.getCharCode());
9693             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9694             
9695         }
9696     },
9697     adjustWidth : function(tag, w){
9698         tag = tag.toLowerCase();
9699         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9700             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9701                 if(tag == 'input'){
9702                     return w + 2;
9703                 }
9704                 if(tag == 'textarea'){
9705                     return w-2;
9706                 }
9707             }else if(Roo.isOpera){
9708                 if(tag == 'input'){
9709                     return w + 2;
9710                 }
9711                 if(tag == 'textarea'){
9712                     return w-2;
9713                 }
9714             }
9715         }
9716         return w;
9717     },
9718     
9719     setFieldLabel : function(v)
9720     {
9721         if(!this.rendered){
9722             return;
9723         }
9724         
9725         if(this.indicator){
9726             var ar = this.el.select('label > span',true);
9727             
9728             if (ar.elements.length) {
9729                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9730                 this.fieldLabel = v;
9731                 return;
9732             }
9733             
9734             var br = this.el.select('label',true);
9735             
9736             if(br.elements.length) {
9737                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9738                 this.fieldLabel = v;
9739                 return;
9740             }
9741             
9742             Roo.log('Cannot Found any of label > span || label in input');
9743             return;
9744         }
9745         
9746         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9747         this.fieldLabel = v;
9748         
9749         
9750     }
9751 });
9752
9753  
9754 /*
9755  * - LGPL
9756  *
9757  * Input
9758  * 
9759  */
9760
9761 /**
9762  * @class Roo.bootstrap.TextArea
9763  * @extends Roo.bootstrap.Input
9764  * Bootstrap TextArea class
9765  * @cfg {Number} cols Specifies the visible width of a text area
9766  * @cfg {Number} rows Specifies the visible number of lines in a text area
9767  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9768  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9769  * @cfg {string} html text
9770  * 
9771  * @constructor
9772  * Create a new TextArea
9773  * @param {Object} config The config object
9774  */
9775
9776 Roo.bootstrap.TextArea = function(config){
9777     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9778    
9779 };
9780
9781 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9782      
9783     cols : false,
9784     rows : 5,
9785     readOnly : false,
9786     warp : 'soft',
9787     resize : false,
9788     value: false,
9789     html: false,
9790     
9791     getAutoCreate : function(){
9792         
9793         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9794         
9795         var id = Roo.id();
9796         
9797         var cfg = {};
9798         
9799         if(this.inputType != 'hidden'){
9800             cfg.cls = 'form-group' //input-group
9801         }
9802         
9803         var input =  {
9804             tag: 'textarea',
9805             id : id,
9806             warp : this.warp,
9807             rows : this.rows,
9808             value : this.value || '',
9809             html: this.html || '',
9810             cls : 'form-control',
9811             placeholder : this.placeholder || '' 
9812             
9813         };
9814         
9815         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9816             input.maxLength = this.maxLength;
9817         }
9818         
9819         if(this.resize){
9820             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9821         }
9822         
9823         if(this.cols){
9824             input.cols = this.cols;
9825         }
9826         
9827         if (this.readOnly) {
9828             input.readonly = true;
9829         }
9830         
9831         if (this.name) {
9832             input.name = this.name;
9833         }
9834         
9835         if (this.size) {
9836             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9837         }
9838         
9839         var settings=this;
9840         ['xs','sm','md','lg'].map(function(size){
9841             if (settings[size]) {
9842                 cfg.cls += ' col-' + size + '-' + settings[size];
9843             }
9844         });
9845         
9846         var inputblock = input;
9847         
9848         if(this.hasFeedback && !this.allowBlank){
9849             
9850             var feedback = {
9851                 tag: 'span',
9852                 cls: 'glyphicon form-control-feedback'
9853             };
9854
9855             inputblock = {
9856                 cls : 'has-feedback',
9857                 cn :  [
9858                     input,
9859                     feedback
9860                 ] 
9861             };  
9862         }
9863         
9864         
9865         if (this.before || this.after) {
9866             
9867             inputblock = {
9868                 cls : 'input-group',
9869                 cn :  [] 
9870             };
9871             if (this.before) {
9872                 inputblock.cn.push({
9873                     tag :'span',
9874                     cls : 'input-group-addon',
9875                     html : this.before
9876                 });
9877             }
9878             
9879             inputblock.cn.push(input);
9880             
9881             if(this.hasFeedback && !this.allowBlank){
9882                 inputblock.cls += ' has-feedback';
9883                 inputblock.cn.push(feedback);
9884             }
9885             
9886             if (this.after) {
9887                 inputblock.cn.push({
9888                     tag :'span',
9889                     cls : 'input-group-addon',
9890                     html : this.after
9891                 });
9892             }
9893             
9894         }
9895         
9896         if (align ==='left' && this.fieldLabel.length) {
9897             cfg.cn = [
9898                 {
9899                     tag: 'label',
9900                     'for' :  id,
9901                     cls : 'control-label',
9902                     html : this.fieldLabel
9903                 },
9904                 {
9905                     cls : "",
9906                     cn: [
9907                         inputblock
9908                     ]
9909                 }
9910
9911             ];
9912             
9913             if(this.labelWidth > 12){
9914                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9915             }
9916
9917             if(this.labelWidth < 13 && this.labelmd == 0){
9918                 this.labelmd = this.labelWidth;
9919             }
9920
9921             if(this.labellg > 0){
9922                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9923                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9924             }
9925
9926             if(this.labelmd > 0){
9927                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9928                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9929             }
9930
9931             if(this.labelsm > 0){
9932                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9933                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9934             }
9935
9936             if(this.labelxs > 0){
9937                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9938                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9939             }
9940             
9941         } else if ( this.fieldLabel.length) {
9942             cfg.cn = [
9943
9944                {
9945                    tag: 'label',
9946                    //cls : 'input-group-addon',
9947                    html : this.fieldLabel
9948
9949                },
9950
9951                inputblock
9952
9953            ];
9954
9955         } else {
9956
9957             cfg.cn = [
9958
9959                 inputblock
9960
9961             ];
9962                 
9963         }
9964         
9965         if (this.disabled) {
9966             input.disabled=true;
9967         }
9968         
9969         return cfg;
9970         
9971     },
9972     /**
9973      * return the real textarea element.
9974      */
9975     inputEl: function ()
9976     {
9977         return this.el.select('textarea.form-control',true).first();
9978     },
9979     
9980     /**
9981      * Clear any invalid styles/messages for this field
9982      */
9983     clearInvalid : function()
9984     {
9985         
9986         if(!this.el || this.preventMark){ // not rendered
9987             return;
9988         }
9989         
9990         var label = this.el.select('label', true).first();
9991         var icon = this.el.select('i.fa-star', true).first();
9992         
9993         if(label && icon){
9994             icon.remove();
9995         }
9996         
9997         this.el.removeClass(this.invalidClass);
9998         
9999         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10000             
10001             var feedback = this.el.select('.form-control-feedback', true).first();
10002             
10003             if(feedback){
10004                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10005             }
10006             
10007         }
10008         
10009         this.fireEvent('valid', this);
10010     },
10011     
10012      /**
10013      * Mark this field as valid
10014      */
10015     markValid : function()
10016     {
10017         if(!this.el  || this.preventMark){ // not rendered
10018             return;
10019         }
10020         
10021         this.el.removeClass([this.invalidClass, this.validClass]);
10022         
10023         var feedback = this.el.select('.form-control-feedback', true).first();
10024             
10025         if(feedback){
10026             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10027         }
10028
10029         if(this.disabled || this.allowBlank){
10030             return;
10031         }
10032         
10033         var label = this.el.select('label', true).first();
10034         var icon = this.el.select('i.fa-star', true).first();
10035         
10036         if(label && icon){
10037             icon.remove();
10038         }
10039         
10040         this.el.addClass(this.validClass);
10041         
10042         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10043             
10044             var feedback = this.el.select('.form-control-feedback', true).first();
10045             
10046             if(feedback){
10047                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10048                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10049             }
10050             
10051         }
10052         
10053         this.fireEvent('valid', this);
10054     },
10055     
10056      /**
10057      * Mark this field as invalid
10058      * @param {String} msg The validation message
10059      */
10060     markInvalid : function(msg)
10061     {
10062         if(!this.el  || this.preventMark){ // not rendered
10063             return;
10064         }
10065         
10066         this.el.removeClass([this.invalidClass, this.validClass]);
10067         
10068         var feedback = this.el.select('.form-control-feedback', true).first();
10069             
10070         if(feedback){
10071             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10072         }
10073
10074         if(this.disabled || this.allowBlank){
10075             return;
10076         }
10077         
10078         var label = this.el.select('label', true).first();
10079         var icon = this.el.select('i.fa-star', true).first();
10080         
10081         if(!this.getValue().length && label && !icon){
10082             this.el.createChild({
10083                 tag : 'i',
10084                 cls : 'text-danger fa fa-lg fa-star',
10085                 tooltip : 'This field is required',
10086                 style : 'margin-right:5px;'
10087             }, label, true);
10088         }
10089
10090         this.el.addClass(this.invalidClass);
10091         
10092         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10093             
10094             var feedback = this.el.select('.form-control-feedback', true).first();
10095             
10096             if(feedback){
10097                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10098                 
10099                 if(this.getValue().length || this.forceFeedback){
10100                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10101                 }
10102                 
10103             }
10104             
10105         }
10106         
10107         this.fireEvent('invalid', this, msg);
10108     }
10109 });
10110
10111  
10112 /*
10113  * - LGPL
10114  *
10115  * trigger field - base class for combo..
10116  * 
10117  */
10118  
10119 /**
10120  * @class Roo.bootstrap.TriggerField
10121  * @extends Roo.bootstrap.Input
10122  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10123  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10124  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10125  * for which you can provide a custom implementation.  For example:
10126  * <pre><code>
10127 var trigger = new Roo.bootstrap.TriggerField();
10128 trigger.onTriggerClick = myTriggerFn;
10129 trigger.applyTo('my-field');
10130 </code></pre>
10131  *
10132  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10133  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10134  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10135  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10136  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10137
10138  * @constructor
10139  * Create a new TriggerField.
10140  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10141  * to the base TextField)
10142  */
10143 Roo.bootstrap.TriggerField = function(config){
10144     this.mimicing = false;
10145     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10146 };
10147
10148 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10149     /**
10150      * @cfg {String} triggerClass A CSS class to apply to the trigger
10151      */
10152      /**
10153      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10154      */
10155     hideTrigger:false,
10156
10157     /**
10158      * @cfg {Boolean} removable (true|false) special filter default false
10159      */
10160     removable : false,
10161     
10162     /** @cfg {Boolean} grow @hide */
10163     /** @cfg {Number} growMin @hide */
10164     /** @cfg {Number} growMax @hide */
10165
10166     /**
10167      * @hide 
10168      * @method
10169      */
10170     autoSize: Roo.emptyFn,
10171     // private
10172     monitorTab : true,
10173     // private
10174     deferHeight : true,
10175
10176     
10177     actionMode : 'wrap',
10178     
10179     caret : false,
10180     
10181     
10182     getAutoCreate : function(){
10183        
10184         var align = this.labelAlign || this.parentLabelAlign();
10185         
10186         var id = Roo.id();
10187         
10188         var cfg = {
10189             cls: 'form-group' //input-group
10190         };
10191         
10192         
10193         var input =  {
10194             tag: 'input',
10195             id : id,
10196             type : this.inputType,
10197             cls : 'form-control',
10198             autocomplete: 'new-password',
10199             placeholder : this.placeholder || '' 
10200             
10201         };
10202         if (this.name) {
10203             input.name = this.name;
10204         }
10205         if (this.size) {
10206             input.cls += ' input-' + this.size;
10207         }
10208         
10209         if (this.disabled) {
10210             input.disabled=true;
10211         }
10212         
10213         var inputblock = input;
10214         
10215         if(this.hasFeedback && !this.allowBlank){
10216             
10217             var feedback = {
10218                 tag: 'span',
10219                 cls: 'glyphicon form-control-feedback'
10220             };
10221             
10222             if(this.removable && !this.editable && !this.tickable){
10223                 inputblock = {
10224                     cls : 'has-feedback',
10225                     cn :  [
10226                         inputblock,
10227                         {
10228                             tag: 'button',
10229                             html : 'x',
10230                             cls : 'roo-combo-removable-btn close'
10231                         },
10232                         feedback
10233                     ] 
10234                 };
10235             } else {
10236                 inputblock = {
10237                     cls : 'has-feedback',
10238                     cn :  [
10239                         inputblock,
10240                         feedback
10241                     ] 
10242                 };
10243             }
10244
10245         } else {
10246             if(this.removable && !this.editable && !this.tickable){
10247                 inputblock = {
10248                     cls : 'roo-removable',
10249                     cn :  [
10250                         inputblock,
10251                         {
10252                             tag: 'button',
10253                             html : 'x',
10254                             cls : 'roo-combo-removable-btn close'
10255                         }
10256                     ] 
10257                 };
10258             }
10259         }
10260         
10261         if (this.before || this.after) {
10262             
10263             inputblock = {
10264                 cls : 'input-group',
10265                 cn :  [] 
10266             };
10267             if (this.before) {
10268                 inputblock.cn.push({
10269                     tag :'span',
10270                     cls : 'input-group-addon',
10271                     html : this.before
10272                 });
10273             }
10274             
10275             inputblock.cn.push(input);
10276             
10277             if(this.hasFeedback && !this.allowBlank){
10278                 inputblock.cls += ' has-feedback';
10279                 inputblock.cn.push(feedback);
10280             }
10281             
10282             if (this.after) {
10283                 inputblock.cn.push({
10284                     tag :'span',
10285                     cls : 'input-group-addon',
10286                     html : this.after
10287                 });
10288             }
10289             
10290         };
10291         
10292         var box = {
10293             tag: 'div',
10294             cn: [
10295                 {
10296                     tag: 'input',
10297                     type : 'hidden',
10298                     cls: 'form-hidden-field'
10299                 },
10300                 inputblock
10301             ]
10302             
10303         };
10304         
10305         if(this.multiple){
10306             box = {
10307                 tag: 'div',
10308                 cn: [
10309                     {
10310                         tag: 'input',
10311                         type : 'hidden',
10312                         cls: 'form-hidden-field'
10313                     },
10314                     {
10315                         tag: 'ul',
10316                         cls: 'roo-select2-choices',
10317                         cn:[
10318                             {
10319                                 tag: 'li',
10320                                 cls: 'roo-select2-search-field',
10321                                 cn: [
10322
10323                                     inputblock
10324                                 ]
10325                             }
10326                         ]
10327                     }
10328                 ]
10329             }
10330         };
10331         
10332         var combobox = {
10333             cls: 'roo-select2-container input-group',
10334             cn: [
10335                 box
10336 //                {
10337 //                    tag: 'ul',
10338 //                    cls: 'typeahead typeahead-long dropdown-menu',
10339 //                    style: 'display:none'
10340 //                }
10341             ]
10342         };
10343         
10344         if(!this.multiple && this.showToggleBtn){
10345             
10346             var caret = {
10347                         tag: 'span',
10348                         cls: 'caret'
10349              };
10350             if (this.caret != false) {
10351                 caret = {
10352                      tag: 'i',
10353                      cls: 'fa fa-' + this.caret
10354                 };
10355                 
10356             }
10357             
10358             combobox.cn.push({
10359                 tag :'span',
10360                 cls : 'input-group-addon btn dropdown-toggle',
10361                 cn : [
10362                     caret,
10363                     {
10364                         tag: 'span',
10365                         cls: 'combobox-clear',
10366                         cn  : [
10367                             {
10368                                 tag : 'i',
10369                                 cls: 'icon-remove'
10370                             }
10371                         ]
10372                     }
10373                 ]
10374
10375             })
10376         }
10377         
10378         if(this.multiple){
10379             combobox.cls += ' roo-select2-container-multi';
10380         }
10381         
10382         if (align ==='left' && this.fieldLabel.length) {
10383             
10384             cfg.cls += ' roo-form-group-label-left';
10385
10386             cfg.cn = [
10387                 {
10388                     tag : 'i',
10389                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10390                     tooltip : 'This field is required'
10391                 },
10392                 {
10393                     tag: 'label',
10394                     'for' :  id,
10395                     cls : 'control-label',
10396                     html : this.fieldLabel
10397
10398                 },
10399                 {
10400                     cls : "", 
10401                     cn: [
10402                         combobox
10403                     ]
10404                 }
10405
10406             ];
10407             
10408             var labelCfg = cfg.cn[1];
10409             var contentCfg = cfg.cn[2];
10410             
10411             if(this.indicatorpos == 'right'){
10412                 cfg.cn = [
10413                     {
10414                         tag: 'label',
10415                         'for' :  id,
10416                         cls : 'control-label',
10417                         cn : [
10418                             {
10419                                 tag : 'span',
10420                                 html : this.fieldLabel
10421                             },
10422                             {
10423                                 tag : 'i',
10424                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10425                                 tooltip : 'This field is required'
10426                             }
10427                         ]
10428                     },
10429                     {
10430                         cls : "", 
10431                         cn: [
10432                             combobox
10433                         ]
10434                     }
10435
10436                 ];
10437                 
10438                 labelCfg = cfg.cn[0];
10439                 contentCfg = cfg.cn[1];
10440             }
10441             
10442             if(this.labelWidth > 12){
10443                 labelCfg.style = "width: " + this.labelWidth + 'px';
10444             }
10445             
10446             if(this.labelWidth < 13 && this.labelmd == 0){
10447                 this.labelmd = this.labelWidth;
10448             }
10449             
10450             if(this.labellg > 0){
10451                 labelCfg.cls += ' col-lg-' + this.labellg;
10452                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10453             }
10454             
10455             if(this.labelmd > 0){
10456                 labelCfg.cls += ' col-md-' + this.labelmd;
10457                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10458             }
10459             
10460             if(this.labelsm > 0){
10461                 labelCfg.cls += ' col-sm-' + this.labelsm;
10462                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10463             }
10464             
10465             if(this.labelxs > 0){
10466                 labelCfg.cls += ' col-xs-' + this.labelxs;
10467                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10468             }
10469             
10470         } else if ( this.fieldLabel.length) {
10471 //                Roo.log(" label");
10472             cfg.cn = [
10473                 {
10474                    tag : 'i',
10475                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10476                    tooltip : 'This field is required'
10477                },
10478                {
10479                    tag: 'label',
10480                    //cls : 'input-group-addon',
10481                    html : this.fieldLabel
10482
10483                },
10484
10485                combobox
10486
10487             ];
10488             
10489             if(this.indicatorpos == 'right'){
10490                 
10491                 cfg.cn = [
10492                     {
10493                        tag: 'label',
10494                        cn : [
10495                            {
10496                                tag : 'span',
10497                                html : this.fieldLabel
10498                            },
10499                            {
10500                               tag : 'i',
10501                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10502                               tooltip : 'This field is required'
10503                            }
10504                        ]
10505
10506                     },
10507                     combobox
10508
10509                 ];
10510
10511             }
10512
10513         } else {
10514             
10515 //                Roo.log(" no label && no align");
10516                 cfg = combobox
10517                      
10518                 
10519         }
10520         
10521         var settings=this;
10522         ['xs','sm','md','lg'].map(function(size){
10523             if (settings[size]) {
10524                 cfg.cls += ' col-' + size + '-' + settings[size];
10525             }
10526         });
10527         
10528         return cfg;
10529         
10530     },
10531     
10532     
10533     
10534     // private
10535     onResize : function(w, h){
10536 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10537 //        if(typeof w == 'number'){
10538 //            var x = w - this.trigger.getWidth();
10539 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10540 //            this.trigger.setStyle('left', x+'px');
10541 //        }
10542     },
10543
10544     // private
10545     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10546
10547     // private
10548     getResizeEl : function(){
10549         return this.inputEl();
10550     },
10551
10552     // private
10553     getPositionEl : function(){
10554         return this.inputEl();
10555     },
10556
10557     // private
10558     alignErrorIcon : function(){
10559         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10560     },
10561
10562     // private
10563     initEvents : function(){
10564         
10565         this.createList();
10566         
10567         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10568         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10569         if(!this.multiple && this.showToggleBtn){
10570             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10571             if(this.hideTrigger){
10572                 this.trigger.setDisplayed(false);
10573             }
10574             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10575         }
10576         
10577         if(this.multiple){
10578             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10579         }
10580         
10581         if(this.removable && !this.editable && !this.tickable){
10582             var close = this.closeTriggerEl();
10583             
10584             if(close){
10585                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10586                 close.on('click', this.removeBtnClick, this, close);
10587             }
10588         }
10589         
10590         //this.trigger.addClassOnOver('x-form-trigger-over');
10591         //this.trigger.addClassOnClick('x-form-trigger-click');
10592         
10593         //if(!this.width){
10594         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10595         //}
10596     },
10597     
10598     closeTriggerEl : function()
10599     {
10600         var close = this.el.select('.roo-combo-removable-btn', true).first();
10601         return close ? close : false;
10602     },
10603     
10604     removeBtnClick : function(e, h, el)
10605     {
10606         e.preventDefault();
10607         
10608         if(this.fireEvent("remove", this) !== false){
10609             this.reset();
10610             this.fireEvent("afterremove", this)
10611         }
10612     },
10613     
10614     createList : function()
10615     {
10616         this.list = Roo.get(document.body).createChild({
10617             tag: 'ul',
10618             cls: 'typeahead typeahead-long dropdown-menu',
10619             style: 'display:none'
10620         });
10621         
10622         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10623         
10624     },
10625
10626     // private
10627     initTrigger : function(){
10628        
10629     },
10630
10631     // private
10632     onDestroy : function(){
10633         if(this.trigger){
10634             this.trigger.removeAllListeners();
10635           //  this.trigger.remove();
10636         }
10637         //if(this.wrap){
10638         //    this.wrap.remove();
10639         //}
10640         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10641     },
10642
10643     // private
10644     onFocus : function(){
10645         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10646         /*
10647         if(!this.mimicing){
10648             this.wrap.addClass('x-trigger-wrap-focus');
10649             this.mimicing = true;
10650             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10651             if(this.monitorTab){
10652                 this.el.on("keydown", this.checkTab, this);
10653             }
10654         }
10655         */
10656     },
10657
10658     // private
10659     checkTab : function(e){
10660         if(e.getKey() == e.TAB){
10661             this.triggerBlur();
10662         }
10663     },
10664
10665     // private
10666     onBlur : function(){
10667         // do nothing
10668     },
10669
10670     // private
10671     mimicBlur : function(e, t){
10672         /*
10673         if(!this.wrap.contains(t) && this.validateBlur()){
10674             this.triggerBlur();
10675         }
10676         */
10677     },
10678
10679     // private
10680     triggerBlur : function(){
10681         this.mimicing = false;
10682         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10683         if(this.monitorTab){
10684             this.el.un("keydown", this.checkTab, this);
10685         }
10686         //this.wrap.removeClass('x-trigger-wrap-focus');
10687         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10688     },
10689
10690     // private
10691     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10692     validateBlur : function(e, t){
10693         return true;
10694     },
10695
10696     // private
10697     onDisable : function(){
10698         this.inputEl().dom.disabled = true;
10699         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10700         //if(this.wrap){
10701         //    this.wrap.addClass('x-item-disabled');
10702         //}
10703     },
10704
10705     // private
10706     onEnable : function(){
10707         this.inputEl().dom.disabled = false;
10708         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10709         //if(this.wrap){
10710         //    this.el.removeClass('x-item-disabled');
10711         //}
10712     },
10713
10714     // private
10715     onShow : function(){
10716         var ae = this.getActionEl();
10717         
10718         if(ae){
10719             ae.dom.style.display = '';
10720             ae.dom.style.visibility = 'visible';
10721         }
10722     },
10723
10724     // private
10725     
10726     onHide : function(){
10727         var ae = this.getActionEl();
10728         ae.dom.style.display = 'none';
10729     },
10730
10731     /**
10732      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10733      * by an implementing function.
10734      * @method
10735      * @param {EventObject} e
10736      */
10737     onTriggerClick : Roo.emptyFn
10738 });
10739  /*
10740  * Based on:
10741  * Ext JS Library 1.1.1
10742  * Copyright(c) 2006-2007, Ext JS, LLC.
10743  *
10744  * Originally Released Under LGPL - original licence link has changed is not relivant.
10745  *
10746  * Fork - LGPL
10747  * <script type="text/javascript">
10748  */
10749
10750
10751 /**
10752  * @class Roo.data.SortTypes
10753  * @singleton
10754  * Defines the default sorting (casting?) comparison functions used when sorting data.
10755  */
10756 Roo.data.SortTypes = {
10757     /**
10758      * Default sort that does nothing
10759      * @param {Mixed} s The value being converted
10760      * @return {Mixed} The comparison value
10761      */
10762     none : function(s){
10763         return s;
10764     },
10765     
10766     /**
10767      * The regular expression used to strip tags
10768      * @type {RegExp}
10769      * @property
10770      */
10771     stripTagsRE : /<\/?[^>]+>/gi,
10772     
10773     /**
10774      * Strips all HTML tags to sort on text only
10775      * @param {Mixed} s The value being converted
10776      * @return {String} The comparison value
10777      */
10778     asText : function(s){
10779         return String(s).replace(this.stripTagsRE, "");
10780     },
10781     
10782     /**
10783      * Strips all HTML tags to sort on text only - Case insensitive
10784      * @param {Mixed} s The value being converted
10785      * @return {String} The comparison value
10786      */
10787     asUCText : function(s){
10788         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10789     },
10790     
10791     /**
10792      * Case insensitive string
10793      * @param {Mixed} s The value being converted
10794      * @return {String} The comparison value
10795      */
10796     asUCString : function(s) {
10797         return String(s).toUpperCase();
10798     },
10799     
10800     /**
10801      * Date sorting
10802      * @param {Mixed} s The value being converted
10803      * @return {Number} The comparison value
10804      */
10805     asDate : function(s) {
10806         if(!s){
10807             return 0;
10808         }
10809         if(s instanceof Date){
10810             return s.getTime();
10811         }
10812         return Date.parse(String(s));
10813     },
10814     
10815     /**
10816      * Float sorting
10817      * @param {Mixed} s The value being converted
10818      * @return {Float} The comparison value
10819      */
10820     asFloat : function(s) {
10821         var val = parseFloat(String(s).replace(/,/g, ""));
10822         if(isNaN(val)) {
10823             val = 0;
10824         }
10825         return val;
10826     },
10827     
10828     /**
10829      * Integer sorting
10830      * @param {Mixed} s The value being converted
10831      * @return {Number} The comparison value
10832      */
10833     asInt : function(s) {
10834         var val = parseInt(String(s).replace(/,/g, ""));
10835         if(isNaN(val)) {
10836             val = 0;
10837         }
10838         return val;
10839     }
10840 };/*
10841  * Based on:
10842  * Ext JS Library 1.1.1
10843  * Copyright(c) 2006-2007, Ext JS, LLC.
10844  *
10845  * Originally Released Under LGPL - original licence link has changed is not relivant.
10846  *
10847  * Fork - LGPL
10848  * <script type="text/javascript">
10849  */
10850
10851 /**
10852 * @class Roo.data.Record
10853  * Instances of this class encapsulate both record <em>definition</em> information, and record
10854  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10855  * to access Records cached in an {@link Roo.data.Store} object.<br>
10856  * <p>
10857  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10858  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10859  * objects.<br>
10860  * <p>
10861  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10862  * @constructor
10863  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10864  * {@link #create}. The parameters are the same.
10865  * @param {Array} data An associative Array of data values keyed by the field name.
10866  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10867  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10868  * not specified an integer id is generated.
10869  */
10870 Roo.data.Record = function(data, id){
10871     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10872     this.data = data;
10873 };
10874
10875 /**
10876  * Generate a constructor for a specific record layout.
10877  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10878  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10879  * Each field definition object may contain the following properties: <ul>
10880  * <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,
10881  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10882  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10883  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10884  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10885  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10886  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10887  * this may be omitted.</p></li>
10888  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10889  * <ul><li>auto (Default, implies no conversion)</li>
10890  * <li>string</li>
10891  * <li>int</li>
10892  * <li>float</li>
10893  * <li>boolean</li>
10894  * <li>date</li></ul></p></li>
10895  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10896  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10897  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10898  * by the Reader into an object that will be stored in the Record. It is passed the
10899  * following parameters:<ul>
10900  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10901  * </ul></p></li>
10902  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10903  * </ul>
10904  * <br>usage:<br><pre><code>
10905 var TopicRecord = Roo.data.Record.create(
10906     {name: 'title', mapping: 'topic_title'},
10907     {name: 'author', mapping: 'username'},
10908     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10909     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10910     {name: 'lastPoster', mapping: 'user2'},
10911     {name: 'excerpt', mapping: 'post_text'}
10912 );
10913
10914 var myNewRecord = new TopicRecord({
10915     title: 'Do my job please',
10916     author: 'noobie',
10917     totalPosts: 1,
10918     lastPost: new Date(),
10919     lastPoster: 'Animal',
10920     excerpt: 'No way dude!'
10921 });
10922 myStore.add(myNewRecord);
10923 </code></pre>
10924  * @method create
10925  * @static
10926  */
10927 Roo.data.Record.create = function(o){
10928     var f = function(){
10929         f.superclass.constructor.apply(this, arguments);
10930     };
10931     Roo.extend(f, Roo.data.Record);
10932     var p = f.prototype;
10933     p.fields = new Roo.util.MixedCollection(false, function(field){
10934         return field.name;
10935     });
10936     for(var i = 0, len = o.length; i < len; i++){
10937         p.fields.add(new Roo.data.Field(o[i]));
10938     }
10939     f.getField = function(name){
10940         return p.fields.get(name);  
10941     };
10942     return f;
10943 };
10944
10945 Roo.data.Record.AUTO_ID = 1000;
10946 Roo.data.Record.EDIT = 'edit';
10947 Roo.data.Record.REJECT = 'reject';
10948 Roo.data.Record.COMMIT = 'commit';
10949
10950 Roo.data.Record.prototype = {
10951     /**
10952      * Readonly flag - true if this record has been modified.
10953      * @type Boolean
10954      */
10955     dirty : false,
10956     editing : false,
10957     error: null,
10958     modified: null,
10959
10960     // private
10961     join : function(store){
10962         this.store = store;
10963     },
10964
10965     /**
10966      * Set the named field to the specified value.
10967      * @param {String} name The name of the field to set.
10968      * @param {Object} value The value to set the field to.
10969      */
10970     set : function(name, value){
10971         if(this.data[name] == value){
10972             return;
10973         }
10974         this.dirty = true;
10975         if(!this.modified){
10976             this.modified = {};
10977         }
10978         if(typeof this.modified[name] == 'undefined'){
10979             this.modified[name] = this.data[name];
10980         }
10981         this.data[name] = value;
10982         if(!this.editing && this.store){
10983             this.store.afterEdit(this);
10984         }       
10985     },
10986
10987     /**
10988      * Get the value of the named field.
10989      * @param {String} name The name of the field to get the value of.
10990      * @return {Object} The value of the field.
10991      */
10992     get : function(name){
10993         return this.data[name]; 
10994     },
10995
10996     // private
10997     beginEdit : function(){
10998         this.editing = true;
10999         this.modified = {}; 
11000     },
11001
11002     // private
11003     cancelEdit : function(){
11004         this.editing = false;
11005         delete this.modified;
11006     },
11007
11008     // private
11009     endEdit : function(){
11010         this.editing = false;
11011         if(this.dirty && this.store){
11012             this.store.afterEdit(this);
11013         }
11014     },
11015
11016     /**
11017      * Usually called by the {@link Roo.data.Store} which owns the Record.
11018      * Rejects all changes made to the Record since either creation, or the last commit operation.
11019      * Modified fields are reverted to their original values.
11020      * <p>
11021      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11022      * of reject operations.
11023      */
11024     reject : function(){
11025         var m = this.modified;
11026         for(var n in m){
11027             if(typeof m[n] != "function"){
11028                 this.data[n] = m[n];
11029             }
11030         }
11031         this.dirty = false;
11032         delete this.modified;
11033         this.editing = false;
11034         if(this.store){
11035             this.store.afterReject(this);
11036         }
11037     },
11038
11039     /**
11040      * Usually called by the {@link Roo.data.Store} which owns the Record.
11041      * Commits all changes made to the Record since either creation, or the last commit operation.
11042      * <p>
11043      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11044      * of commit operations.
11045      */
11046     commit : function(){
11047         this.dirty = false;
11048         delete this.modified;
11049         this.editing = false;
11050         if(this.store){
11051             this.store.afterCommit(this);
11052         }
11053     },
11054
11055     // private
11056     hasError : function(){
11057         return this.error != null;
11058     },
11059
11060     // private
11061     clearError : function(){
11062         this.error = null;
11063     },
11064
11065     /**
11066      * Creates a copy of this record.
11067      * @param {String} id (optional) A new record id if you don't want to use this record's id
11068      * @return {Record}
11069      */
11070     copy : function(newId) {
11071         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11072     }
11073 };/*
11074  * Based on:
11075  * Ext JS Library 1.1.1
11076  * Copyright(c) 2006-2007, Ext JS, LLC.
11077  *
11078  * Originally Released Under LGPL - original licence link has changed is not relivant.
11079  *
11080  * Fork - LGPL
11081  * <script type="text/javascript">
11082  */
11083
11084
11085
11086 /**
11087  * @class Roo.data.Store
11088  * @extends Roo.util.Observable
11089  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11090  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11091  * <p>
11092  * 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
11093  * has no knowledge of the format of the data returned by the Proxy.<br>
11094  * <p>
11095  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11096  * instances from the data object. These records are cached and made available through accessor functions.
11097  * @constructor
11098  * Creates a new Store.
11099  * @param {Object} config A config object containing the objects needed for the Store to access data,
11100  * and read the data into Records.
11101  */
11102 Roo.data.Store = function(config){
11103     this.data = new Roo.util.MixedCollection(false);
11104     this.data.getKey = function(o){
11105         return o.id;
11106     };
11107     this.baseParams = {};
11108     // private
11109     this.paramNames = {
11110         "start" : "start",
11111         "limit" : "limit",
11112         "sort" : "sort",
11113         "dir" : "dir",
11114         "multisort" : "_multisort"
11115     };
11116
11117     if(config && config.data){
11118         this.inlineData = config.data;
11119         delete config.data;
11120     }
11121
11122     Roo.apply(this, config);
11123     
11124     if(this.reader){ // reader passed
11125         this.reader = Roo.factory(this.reader, Roo.data);
11126         this.reader.xmodule = this.xmodule || false;
11127         if(!this.recordType){
11128             this.recordType = this.reader.recordType;
11129         }
11130         if(this.reader.onMetaChange){
11131             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11132         }
11133     }
11134
11135     if(this.recordType){
11136         this.fields = this.recordType.prototype.fields;
11137     }
11138     this.modified = [];
11139
11140     this.addEvents({
11141         /**
11142          * @event datachanged
11143          * Fires when the data cache has changed, and a widget which is using this Store
11144          * as a Record cache should refresh its view.
11145          * @param {Store} this
11146          */
11147         datachanged : true,
11148         /**
11149          * @event metachange
11150          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11151          * @param {Store} this
11152          * @param {Object} meta The JSON metadata
11153          */
11154         metachange : true,
11155         /**
11156          * @event add
11157          * Fires when Records have been added to the Store
11158          * @param {Store} this
11159          * @param {Roo.data.Record[]} records The array of Records added
11160          * @param {Number} index The index at which the record(s) were added
11161          */
11162         add : true,
11163         /**
11164          * @event remove
11165          * Fires when a Record has been removed from the Store
11166          * @param {Store} this
11167          * @param {Roo.data.Record} record The Record that was removed
11168          * @param {Number} index The index at which the record was removed
11169          */
11170         remove : true,
11171         /**
11172          * @event update
11173          * Fires when a Record has been updated
11174          * @param {Store} this
11175          * @param {Roo.data.Record} record The Record that was updated
11176          * @param {String} operation The update operation being performed.  Value may be one of:
11177          * <pre><code>
11178  Roo.data.Record.EDIT
11179  Roo.data.Record.REJECT
11180  Roo.data.Record.COMMIT
11181          * </code></pre>
11182          */
11183         update : true,
11184         /**
11185          * @event clear
11186          * Fires when the data cache has been cleared.
11187          * @param {Store} this
11188          */
11189         clear : true,
11190         /**
11191          * @event beforeload
11192          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11193          * the load action will be canceled.
11194          * @param {Store} this
11195          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11196          */
11197         beforeload : true,
11198         /**
11199          * @event beforeloadadd
11200          * Fires after a new set of Records has been loaded.
11201          * @param {Store} this
11202          * @param {Roo.data.Record[]} records The Records that were loaded
11203          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11204          */
11205         beforeloadadd : true,
11206         /**
11207          * @event load
11208          * Fires after a new set of Records has been loaded, before they are added to the store.
11209          * @param {Store} this
11210          * @param {Roo.data.Record[]} records The Records that were loaded
11211          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11212          * @params {Object} return from reader
11213          */
11214         load : true,
11215         /**
11216          * @event loadexception
11217          * Fires if an exception occurs in the Proxy during loading.
11218          * Called with the signature of the Proxy's "loadexception" event.
11219          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11220          * 
11221          * @param {Proxy} 
11222          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11223          * @param {Object} load options 
11224          * @param {Object} jsonData from your request (normally this contains the Exception)
11225          */
11226         loadexception : true
11227     });
11228     
11229     if(this.proxy){
11230         this.proxy = Roo.factory(this.proxy, Roo.data);
11231         this.proxy.xmodule = this.xmodule || false;
11232         this.relayEvents(this.proxy,  ["loadexception"]);
11233     }
11234     this.sortToggle = {};
11235     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11236
11237     Roo.data.Store.superclass.constructor.call(this);
11238
11239     if(this.inlineData){
11240         this.loadData(this.inlineData);
11241         delete this.inlineData;
11242     }
11243 };
11244
11245 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11246      /**
11247     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11248     * without a remote query - used by combo/forms at present.
11249     */
11250     
11251     /**
11252     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11253     */
11254     /**
11255     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11256     */
11257     /**
11258     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11259     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11260     */
11261     /**
11262     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11263     * on any HTTP request
11264     */
11265     /**
11266     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11267     */
11268     /**
11269     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11270     */
11271     multiSort: false,
11272     /**
11273     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11274     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11275     */
11276     remoteSort : false,
11277
11278     /**
11279     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11280      * loaded or when a record is removed. (defaults to false).
11281     */
11282     pruneModifiedRecords : false,
11283
11284     // private
11285     lastOptions : null,
11286
11287     /**
11288      * Add Records to the Store and fires the add event.
11289      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11290      */
11291     add : function(records){
11292         records = [].concat(records);
11293         for(var i = 0, len = records.length; i < len; i++){
11294             records[i].join(this);
11295         }
11296         var index = this.data.length;
11297         this.data.addAll(records);
11298         this.fireEvent("add", this, records, index);
11299     },
11300
11301     /**
11302      * Remove a Record from the Store and fires the remove event.
11303      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11304      */
11305     remove : function(record){
11306         var index = this.data.indexOf(record);
11307         this.data.removeAt(index);
11308  
11309         if(this.pruneModifiedRecords){
11310             this.modified.remove(record);
11311         }
11312         this.fireEvent("remove", this, record, index);
11313     },
11314
11315     /**
11316      * Remove all Records from the Store and fires the clear event.
11317      */
11318     removeAll : function(){
11319         this.data.clear();
11320         if(this.pruneModifiedRecords){
11321             this.modified = [];
11322         }
11323         this.fireEvent("clear", this);
11324     },
11325
11326     /**
11327      * Inserts Records to the Store at the given index and fires the add event.
11328      * @param {Number} index The start index at which to insert the passed Records.
11329      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11330      */
11331     insert : function(index, records){
11332         records = [].concat(records);
11333         for(var i = 0, len = records.length; i < len; i++){
11334             this.data.insert(index, records[i]);
11335             records[i].join(this);
11336         }
11337         this.fireEvent("add", this, records, index);
11338     },
11339
11340     /**
11341      * Get the index within the cache of the passed Record.
11342      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11343      * @return {Number} The index of the passed Record. Returns -1 if not found.
11344      */
11345     indexOf : function(record){
11346         return this.data.indexOf(record);
11347     },
11348
11349     /**
11350      * Get the index within the cache of the Record with the passed id.
11351      * @param {String} id The id of the Record to find.
11352      * @return {Number} The index of the Record. Returns -1 if not found.
11353      */
11354     indexOfId : function(id){
11355         return this.data.indexOfKey(id);
11356     },
11357
11358     /**
11359      * Get the Record with the specified id.
11360      * @param {String} id The id of the Record to find.
11361      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11362      */
11363     getById : function(id){
11364         return this.data.key(id);
11365     },
11366
11367     /**
11368      * Get the Record at the specified index.
11369      * @param {Number} index The index of the Record to find.
11370      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11371      */
11372     getAt : function(index){
11373         return this.data.itemAt(index);
11374     },
11375
11376     /**
11377      * Returns a range of Records between specified indices.
11378      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11379      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11380      * @return {Roo.data.Record[]} An array of Records
11381      */
11382     getRange : function(start, end){
11383         return this.data.getRange(start, end);
11384     },
11385
11386     // private
11387     storeOptions : function(o){
11388         o = Roo.apply({}, o);
11389         delete o.callback;
11390         delete o.scope;
11391         this.lastOptions = o;
11392     },
11393
11394     /**
11395      * Loads the Record cache from the configured Proxy using the configured Reader.
11396      * <p>
11397      * If using remote paging, then the first load call must specify the <em>start</em>
11398      * and <em>limit</em> properties in the options.params property to establish the initial
11399      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11400      * <p>
11401      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11402      * and this call will return before the new data has been loaded. Perform any post-processing
11403      * in a callback function, or in a "load" event handler.</strong>
11404      * <p>
11405      * @param {Object} options An object containing properties which control loading options:<ul>
11406      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11407      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11408      * passed the following arguments:<ul>
11409      * <li>r : Roo.data.Record[]</li>
11410      * <li>options: Options object from the load call</li>
11411      * <li>success: Boolean success indicator</li></ul></li>
11412      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11413      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11414      * </ul>
11415      */
11416     load : function(options){
11417         options = options || {};
11418         if(this.fireEvent("beforeload", this, options) !== false){
11419             this.storeOptions(options);
11420             var p = Roo.apply(options.params || {}, this.baseParams);
11421             // if meta was not loaded from remote source.. try requesting it.
11422             if (!this.reader.metaFromRemote) {
11423                 p._requestMeta = 1;
11424             }
11425             if(this.sortInfo && this.remoteSort){
11426                 var pn = this.paramNames;
11427                 p[pn["sort"]] = this.sortInfo.field;
11428                 p[pn["dir"]] = this.sortInfo.direction;
11429             }
11430             if (this.multiSort) {
11431                 var pn = this.paramNames;
11432                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11433             }
11434             
11435             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11436         }
11437     },
11438
11439     /**
11440      * Reloads the Record cache from the configured Proxy using the configured Reader and
11441      * the options from the last load operation performed.
11442      * @param {Object} options (optional) An object containing properties which may override the options
11443      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11444      * the most recently used options are reused).
11445      */
11446     reload : function(options){
11447         this.load(Roo.applyIf(options||{}, this.lastOptions));
11448     },
11449
11450     // private
11451     // Called as a callback by the Reader during a load operation.
11452     loadRecords : function(o, options, success){
11453         if(!o || success === false){
11454             if(success !== false){
11455                 this.fireEvent("load", this, [], options, o);
11456             }
11457             if(options.callback){
11458                 options.callback.call(options.scope || this, [], options, false);
11459             }
11460             return;
11461         }
11462         // if data returned failure - throw an exception.
11463         if (o.success === false) {
11464             // show a message if no listener is registered.
11465             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11466                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11467             }
11468             // loadmask wil be hooked into this..
11469             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11470             return;
11471         }
11472         var r = o.records, t = o.totalRecords || r.length;
11473         
11474         this.fireEvent("beforeloadadd", this, r, options, o);
11475         
11476         if(!options || options.add !== true){
11477             if(this.pruneModifiedRecords){
11478                 this.modified = [];
11479             }
11480             for(var i = 0, len = r.length; i < len; i++){
11481                 r[i].join(this);
11482             }
11483             if(this.snapshot){
11484                 this.data = this.snapshot;
11485                 delete this.snapshot;
11486             }
11487             this.data.clear();
11488             this.data.addAll(r);
11489             this.totalLength = t;
11490             this.applySort();
11491             this.fireEvent("datachanged", this);
11492         }else{
11493             this.totalLength = Math.max(t, this.data.length+r.length);
11494             this.add(r);
11495         }
11496         
11497         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11498                 
11499             var e = new Roo.data.Record({});
11500
11501             e.set(this.parent.displayField, this.parent.emptyTitle);
11502             e.set(this.parent.valueField, '');
11503
11504             this.insert(0, e);
11505         }
11506             
11507         this.fireEvent("load", this, r, options, o);
11508         if(options.callback){
11509             options.callback.call(options.scope || this, r, options, true);
11510         }
11511     },
11512
11513
11514     /**
11515      * Loads data from a passed data block. A Reader which understands the format of the data
11516      * must have been configured in the constructor.
11517      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11518      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11519      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11520      */
11521     loadData : function(o, append){
11522         var r = this.reader.readRecords(o);
11523         this.loadRecords(r, {add: append}, true);
11524     },
11525
11526     /**
11527      * Gets the number of cached records.
11528      * <p>
11529      * <em>If using paging, this may not be the total size of the dataset. If the data object
11530      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11531      * the data set size</em>
11532      */
11533     getCount : function(){
11534         return this.data.length || 0;
11535     },
11536
11537     /**
11538      * Gets the total number of records in the dataset as returned by the server.
11539      * <p>
11540      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11541      * the dataset size</em>
11542      */
11543     getTotalCount : function(){
11544         return this.totalLength || 0;
11545     },
11546
11547     /**
11548      * Returns the sort state of the Store as an object with two properties:
11549      * <pre><code>
11550  field {String} The name of the field by which the Records are sorted
11551  direction {String} The sort order, "ASC" or "DESC"
11552      * </code></pre>
11553      */
11554     getSortState : function(){
11555         return this.sortInfo;
11556     },
11557
11558     // private
11559     applySort : function(){
11560         if(this.sortInfo && !this.remoteSort){
11561             var s = this.sortInfo, f = s.field;
11562             var st = this.fields.get(f).sortType;
11563             var fn = function(r1, r2){
11564                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11565                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11566             };
11567             this.data.sort(s.direction, fn);
11568             if(this.snapshot && this.snapshot != this.data){
11569                 this.snapshot.sort(s.direction, fn);
11570             }
11571         }
11572     },
11573
11574     /**
11575      * Sets the default sort column and order to be used by the next load operation.
11576      * @param {String} fieldName The name of the field to sort by.
11577      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11578      */
11579     setDefaultSort : function(field, dir){
11580         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11581     },
11582
11583     /**
11584      * Sort the Records.
11585      * If remote sorting is used, the sort is performed on the server, and the cache is
11586      * reloaded. If local sorting is used, the cache is sorted internally.
11587      * @param {String} fieldName The name of the field to sort by.
11588      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11589      */
11590     sort : function(fieldName, dir){
11591         var f = this.fields.get(fieldName);
11592         if(!dir){
11593             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11594             
11595             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11596                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11597             }else{
11598                 dir = f.sortDir;
11599             }
11600         }
11601         this.sortToggle[f.name] = dir;
11602         this.sortInfo = {field: f.name, direction: dir};
11603         if(!this.remoteSort){
11604             this.applySort();
11605             this.fireEvent("datachanged", this);
11606         }else{
11607             this.load(this.lastOptions);
11608         }
11609     },
11610
11611     /**
11612      * Calls the specified function for each of the Records in the cache.
11613      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11614      * Returning <em>false</em> aborts and exits the iteration.
11615      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11616      */
11617     each : function(fn, scope){
11618         this.data.each(fn, scope);
11619     },
11620
11621     /**
11622      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11623      * (e.g., during paging).
11624      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11625      */
11626     getModifiedRecords : function(){
11627         return this.modified;
11628     },
11629
11630     // private
11631     createFilterFn : function(property, value, anyMatch){
11632         if(!value.exec){ // not a regex
11633             value = String(value);
11634             if(value.length == 0){
11635                 return false;
11636             }
11637             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11638         }
11639         return function(r){
11640             return value.test(r.data[property]);
11641         };
11642     },
11643
11644     /**
11645      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11646      * @param {String} property A field on your records
11647      * @param {Number} start The record index to start at (defaults to 0)
11648      * @param {Number} end The last record index to include (defaults to length - 1)
11649      * @return {Number} The sum
11650      */
11651     sum : function(property, start, end){
11652         var rs = this.data.items, v = 0;
11653         start = start || 0;
11654         end = (end || end === 0) ? end : rs.length-1;
11655
11656         for(var i = start; i <= end; i++){
11657             v += (rs[i].data[property] || 0);
11658         }
11659         return v;
11660     },
11661
11662     /**
11663      * Filter the records by a specified property.
11664      * @param {String} field A field on your records
11665      * @param {String/RegExp} value Either a string that the field
11666      * should start with or a RegExp to test against the field
11667      * @param {Boolean} anyMatch True to match any part not just the beginning
11668      */
11669     filter : function(property, value, anyMatch){
11670         var fn = this.createFilterFn(property, value, anyMatch);
11671         return fn ? this.filterBy(fn) : this.clearFilter();
11672     },
11673
11674     /**
11675      * Filter by a function. The specified function will be called with each
11676      * record in this data source. If the function returns true the record is included,
11677      * otherwise it is filtered.
11678      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11679      * @param {Object} scope (optional) The scope of the function (defaults to this)
11680      */
11681     filterBy : function(fn, scope){
11682         this.snapshot = this.snapshot || this.data;
11683         this.data = this.queryBy(fn, scope||this);
11684         this.fireEvent("datachanged", this);
11685     },
11686
11687     /**
11688      * Query the records by a specified property.
11689      * @param {String} field A field on your records
11690      * @param {String/RegExp} value Either a string that the field
11691      * should start with or a RegExp to test against the field
11692      * @param {Boolean} anyMatch True to match any part not just the beginning
11693      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11694      */
11695     query : function(property, value, anyMatch){
11696         var fn = this.createFilterFn(property, value, anyMatch);
11697         return fn ? this.queryBy(fn) : this.data.clone();
11698     },
11699
11700     /**
11701      * Query by a function. The specified function will be called with each
11702      * record in this data source. If the function returns true the record is included
11703      * in the results.
11704      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11705      * @param {Object} scope (optional) The scope of the function (defaults to this)
11706       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11707      **/
11708     queryBy : function(fn, scope){
11709         var data = this.snapshot || this.data;
11710         return data.filterBy(fn, scope||this);
11711     },
11712
11713     /**
11714      * Collects unique values for a particular dataIndex from this store.
11715      * @param {String} dataIndex The property to collect
11716      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11717      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11718      * @return {Array} An array of the unique values
11719      **/
11720     collect : function(dataIndex, allowNull, bypassFilter){
11721         var d = (bypassFilter === true && this.snapshot) ?
11722                 this.snapshot.items : this.data.items;
11723         var v, sv, r = [], l = {};
11724         for(var i = 0, len = d.length; i < len; i++){
11725             v = d[i].data[dataIndex];
11726             sv = String(v);
11727             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11728                 l[sv] = true;
11729                 r[r.length] = v;
11730             }
11731         }
11732         return r;
11733     },
11734
11735     /**
11736      * Revert to a view of the Record cache with no filtering applied.
11737      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11738      */
11739     clearFilter : function(suppressEvent){
11740         if(this.snapshot && this.snapshot != this.data){
11741             this.data = this.snapshot;
11742             delete this.snapshot;
11743             if(suppressEvent !== true){
11744                 this.fireEvent("datachanged", this);
11745             }
11746         }
11747     },
11748
11749     // private
11750     afterEdit : function(record){
11751         if(this.modified.indexOf(record) == -1){
11752             this.modified.push(record);
11753         }
11754         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11755     },
11756     
11757     // private
11758     afterReject : function(record){
11759         this.modified.remove(record);
11760         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11761     },
11762
11763     // private
11764     afterCommit : function(record){
11765         this.modified.remove(record);
11766         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11767     },
11768
11769     /**
11770      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11771      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11772      */
11773     commitChanges : function(){
11774         var m = this.modified.slice(0);
11775         this.modified = [];
11776         for(var i = 0, len = m.length; i < len; i++){
11777             m[i].commit();
11778         }
11779     },
11780
11781     /**
11782      * Cancel outstanding changes on all changed records.
11783      */
11784     rejectChanges : function(){
11785         var m = this.modified.slice(0);
11786         this.modified = [];
11787         for(var i = 0, len = m.length; i < len; i++){
11788             m[i].reject();
11789         }
11790     },
11791
11792     onMetaChange : function(meta, rtype, o){
11793         this.recordType = rtype;
11794         this.fields = rtype.prototype.fields;
11795         delete this.snapshot;
11796         this.sortInfo = meta.sortInfo || this.sortInfo;
11797         this.modified = [];
11798         this.fireEvent('metachange', this, this.reader.meta);
11799     },
11800     
11801     moveIndex : function(data, type)
11802     {
11803         var index = this.indexOf(data);
11804         
11805         var newIndex = index + type;
11806         
11807         this.remove(data);
11808         
11809         this.insert(newIndex, data);
11810         
11811     }
11812 });/*
11813  * Based on:
11814  * Ext JS Library 1.1.1
11815  * Copyright(c) 2006-2007, Ext JS, LLC.
11816  *
11817  * Originally Released Under LGPL - original licence link has changed is not relivant.
11818  *
11819  * Fork - LGPL
11820  * <script type="text/javascript">
11821  */
11822
11823 /**
11824  * @class Roo.data.SimpleStore
11825  * @extends Roo.data.Store
11826  * Small helper class to make creating Stores from Array data easier.
11827  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11828  * @cfg {Array} fields An array of field definition objects, or field name strings.
11829  * @cfg {Array} data The multi-dimensional array of data
11830  * @constructor
11831  * @param {Object} config
11832  */
11833 Roo.data.SimpleStore = function(config){
11834     Roo.data.SimpleStore.superclass.constructor.call(this, {
11835         isLocal : true,
11836         reader: new Roo.data.ArrayReader({
11837                 id: config.id
11838             },
11839             Roo.data.Record.create(config.fields)
11840         ),
11841         proxy : new Roo.data.MemoryProxy(config.data)
11842     });
11843     this.load();
11844 };
11845 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11846  * Based on:
11847  * Ext JS Library 1.1.1
11848  * Copyright(c) 2006-2007, Ext JS, LLC.
11849  *
11850  * Originally Released Under LGPL - original licence link has changed is not relivant.
11851  *
11852  * Fork - LGPL
11853  * <script type="text/javascript">
11854  */
11855
11856 /**
11857 /**
11858  * @extends Roo.data.Store
11859  * @class Roo.data.JsonStore
11860  * Small helper class to make creating Stores for JSON data easier. <br/>
11861 <pre><code>
11862 var store = new Roo.data.JsonStore({
11863     url: 'get-images.php',
11864     root: 'images',
11865     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11866 });
11867 </code></pre>
11868  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11869  * JsonReader and HttpProxy (unless inline data is provided).</b>
11870  * @cfg {Array} fields An array of field definition objects, or field name strings.
11871  * @constructor
11872  * @param {Object} config
11873  */
11874 Roo.data.JsonStore = function(c){
11875     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11876         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11877         reader: new Roo.data.JsonReader(c, c.fields)
11878     }));
11879 };
11880 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11881  * Based on:
11882  * Ext JS Library 1.1.1
11883  * Copyright(c) 2006-2007, Ext JS, LLC.
11884  *
11885  * Originally Released Under LGPL - original licence link has changed is not relivant.
11886  *
11887  * Fork - LGPL
11888  * <script type="text/javascript">
11889  */
11890
11891  
11892 Roo.data.Field = function(config){
11893     if(typeof config == "string"){
11894         config = {name: config};
11895     }
11896     Roo.apply(this, config);
11897     
11898     if(!this.type){
11899         this.type = "auto";
11900     }
11901     
11902     var st = Roo.data.SortTypes;
11903     // named sortTypes are supported, here we look them up
11904     if(typeof this.sortType == "string"){
11905         this.sortType = st[this.sortType];
11906     }
11907     
11908     // set default sortType for strings and dates
11909     if(!this.sortType){
11910         switch(this.type){
11911             case "string":
11912                 this.sortType = st.asUCString;
11913                 break;
11914             case "date":
11915                 this.sortType = st.asDate;
11916                 break;
11917             default:
11918                 this.sortType = st.none;
11919         }
11920     }
11921
11922     // define once
11923     var stripRe = /[\$,%]/g;
11924
11925     // prebuilt conversion function for this field, instead of
11926     // switching every time we're reading a value
11927     if(!this.convert){
11928         var cv, dateFormat = this.dateFormat;
11929         switch(this.type){
11930             case "":
11931             case "auto":
11932             case undefined:
11933                 cv = function(v){ return v; };
11934                 break;
11935             case "string":
11936                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11937                 break;
11938             case "int":
11939                 cv = function(v){
11940                     return v !== undefined && v !== null && v !== '' ?
11941                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11942                     };
11943                 break;
11944             case "float":
11945                 cv = function(v){
11946                     return v !== undefined && v !== null && v !== '' ?
11947                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11948                     };
11949                 break;
11950             case "bool":
11951             case "boolean":
11952                 cv = function(v){ return v === true || v === "true" || v == 1; };
11953                 break;
11954             case "date":
11955                 cv = function(v){
11956                     if(!v){
11957                         return '';
11958                     }
11959                     if(v instanceof Date){
11960                         return v;
11961                     }
11962                     if(dateFormat){
11963                         if(dateFormat == "timestamp"){
11964                             return new Date(v*1000);
11965                         }
11966                         return Date.parseDate(v, dateFormat);
11967                     }
11968                     var parsed = Date.parse(v);
11969                     return parsed ? new Date(parsed) : null;
11970                 };
11971              break;
11972             
11973         }
11974         this.convert = cv;
11975     }
11976 };
11977
11978 Roo.data.Field.prototype = {
11979     dateFormat: null,
11980     defaultValue: "",
11981     mapping: null,
11982     sortType : null,
11983     sortDir : "ASC"
11984 };/*
11985  * Based on:
11986  * Ext JS Library 1.1.1
11987  * Copyright(c) 2006-2007, Ext JS, LLC.
11988  *
11989  * Originally Released Under LGPL - original licence link has changed is not relivant.
11990  *
11991  * Fork - LGPL
11992  * <script type="text/javascript">
11993  */
11994  
11995 // Base class for reading structured data from a data source.  This class is intended to be
11996 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11997
11998 /**
11999  * @class Roo.data.DataReader
12000  * Base class for reading structured data from a data source.  This class is intended to be
12001  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12002  */
12003
12004 Roo.data.DataReader = function(meta, recordType){
12005     
12006     this.meta = meta;
12007     
12008     this.recordType = recordType instanceof Array ? 
12009         Roo.data.Record.create(recordType) : recordType;
12010 };
12011
12012 Roo.data.DataReader.prototype = {
12013      /**
12014      * Create an empty record
12015      * @param {Object} data (optional) - overlay some values
12016      * @return {Roo.data.Record} record created.
12017      */
12018     newRow :  function(d) {
12019         var da =  {};
12020         this.recordType.prototype.fields.each(function(c) {
12021             switch( c.type) {
12022                 case 'int' : da[c.name] = 0; break;
12023                 case 'date' : da[c.name] = new Date(); break;
12024                 case 'float' : da[c.name] = 0.0; break;
12025                 case 'boolean' : da[c.name] = false; break;
12026                 default : da[c.name] = ""; break;
12027             }
12028             
12029         });
12030         return new this.recordType(Roo.apply(da, d));
12031     }
12032     
12033 };/*
12034  * Based on:
12035  * Ext JS Library 1.1.1
12036  * Copyright(c) 2006-2007, Ext JS, LLC.
12037  *
12038  * Originally Released Under LGPL - original licence link has changed is not relivant.
12039  *
12040  * Fork - LGPL
12041  * <script type="text/javascript">
12042  */
12043
12044 /**
12045  * @class Roo.data.DataProxy
12046  * @extends Roo.data.Observable
12047  * This class is an abstract base class for implementations which provide retrieval of
12048  * unformatted data objects.<br>
12049  * <p>
12050  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12051  * (of the appropriate type which knows how to parse the data object) to provide a block of
12052  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12053  * <p>
12054  * Custom implementations must implement the load method as described in
12055  * {@link Roo.data.HttpProxy#load}.
12056  */
12057 Roo.data.DataProxy = function(){
12058     this.addEvents({
12059         /**
12060          * @event beforeload
12061          * Fires before a network request is made to retrieve a data object.
12062          * @param {Object} This DataProxy object.
12063          * @param {Object} params The params parameter to the load function.
12064          */
12065         beforeload : true,
12066         /**
12067          * @event load
12068          * Fires before the load method's callback is called.
12069          * @param {Object} This DataProxy object.
12070          * @param {Object} o The data object.
12071          * @param {Object} arg The callback argument object passed to the load function.
12072          */
12073         load : true,
12074         /**
12075          * @event loadexception
12076          * Fires if an Exception occurs during data retrieval.
12077          * @param {Object} This DataProxy object.
12078          * @param {Object} o The data object.
12079          * @param {Object} arg The callback argument object passed to the load function.
12080          * @param {Object} e The Exception.
12081          */
12082         loadexception : true
12083     });
12084     Roo.data.DataProxy.superclass.constructor.call(this);
12085 };
12086
12087 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12088
12089     /**
12090      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12091      */
12092 /*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102 /**
12103  * @class Roo.data.MemoryProxy
12104  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12105  * to the Reader when its load method is called.
12106  * @constructor
12107  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12108  */
12109 Roo.data.MemoryProxy = function(data){
12110     if (data.data) {
12111         data = data.data;
12112     }
12113     Roo.data.MemoryProxy.superclass.constructor.call(this);
12114     this.data = data;
12115 };
12116
12117 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12118     
12119     /**
12120      * Load data from the requested source (in this case an in-memory
12121      * data object passed to the constructor), read the data object into
12122      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12123      * process that block using the passed callback.
12124      * @param {Object} params This parameter is not used by the MemoryProxy class.
12125      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12126      * object into a block of Roo.data.Records.
12127      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12128      * The function must be passed <ul>
12129      * <li>The Record block object</li>
12130      * <li>The "arg" argument from the load function</li>
12131      * <li>A boolean success indicator</li>
12132      * </ul>
12133      * @param {Object} scope The scope in which to call the callback
12134      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12135      */
12136     load : function(params, reader, callback, scope, arg){
12137         params = params || {};
12138         var result;
12139         try {
12140             result = reader.readRecords(this.data);
12141         }catch(e){
12142             this.fireEvent("loadexception", this, arg, null, e);
12143             callback.call(scope, null, arg, false);
12144             return;
12145         }
12146         callback.call(scope, result, arg, true);
12147     },
12148     
12149     // private
12150     update : function(params, records){
12151         
12152     }
12153 });/*
12154  * Based on:
12155  * Ext JS Library 1.1.1
12156  * Copyright(c) 2006-2007, Ext JS, LLC.
12157  *
12158  * Originally Released Under LGPL - original licence link has changed is not relivant.
12159  *
12160  * Fork - LGPL
12161  * <script type="text/javascript">
12162  */
12163 /**
12164  * @class Roo.data.HttpProxy
12165  * @extends Roo.data.DataProxy
12166  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12167  * configured to reference a certain URL.<br><br>
12168  * <p>
12169  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12170  * from which the running page was served.<br><br>
12171  * <p>
12172  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12173  * <p>
12174  * Be aware that to enable the browser to parse an XML document, the server must set
12175  * the Content-Type header in the HTTP response to "text/xml".
12176  * @constructor
12177  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12178  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12179  * will be used to make the request.
12180  */
12181 Roo.data.HttpProxy = function(conn){
12182     Roo.data.HttpProxy.superclass.constructor.call(this);
12183     // is conn a conn config or a real conn?
12184     this.conn = conn;
12185     this.useAjax = !conn || !conn.events;
12186   
12187 };
12188
12189 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12190     // thse are take from connection...
12191     
12192     /**
12193      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12194      */
12195     /**
12196      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12197      * extra parameters to each request made by this object. (defaults to undefined)
12198      */
12199     /**
12200      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12201      *  to each request made by this object. (defaults to undefined)
12202      */
12203     /**
12204      * @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)
12205      */
12206     /**
12207      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12208      */
12209      /**
12210      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12211      * @type Boolean
12212      */
12213   
12214
12215     /**
12216      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12217      * @type Boolean
12218      */
12219     /**
12220      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12221      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12222      * a finer-grained basis than the DataProxy events.
12223      */
12224     getConnection : function(){
12225         return this.useAjax ? Roo.Ajax : this.conn;
12226     },
12227
12228     /**
12229      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12230      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12231      * process that block using the passed callback.
12232      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12233      * for the request to the remote server.
12234      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12235      * object into a block of Roo.data.Records.
12236      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12237      * The function must be passed <ul>
12238      * <li>The Record block object</li>
12239      * <li>The "arg" argument from the load function</li>
12240      * <li>A boolean success indicator</li>
12241      * </ul>
12242      * @param {Object} scope The scope in which to call the callback
12243      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12244      */
12245     load : function(params, reader, callback, scope, arg){
12246         if(this.fireEvent("beforeload", this, params) !== false){
12247             var  o = {
12248                 params : params || {},
12249                 request: {
12250                     callback : callback,
12251                     scope : scope,
12252                     arg : arg
12253                 },
12254                 reader: reader,
12255                 callback : this.loadResponse,
12256                 scope: this
12257             };
12258             if(this.useAjax){
12259                 Roo.applyIf(o, this.conn);
12260                 if(this.activeRequest){
12261                     Roo.Ajax.abort(this.activeRequest);
12262                 }
12263                 this.activeRequest = Roo.Ajax.request(o);
12264             }else{
12265                 this.conn.request(o);
12266             }
12267         }else{
12268             callback.call(scope||this, null, arg, false);
12269         }
12270     },
12271
12272     // private
12273     loadResponse : function(o, success, response){
12274         delete this.activeRequest;
12275         if(!success){
12276             this.fireEvent("loadexception", this, o, response);
12277             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12278             return;
12279         }
12280         var result;
12281         try {
12282             result = o.reader.read(response);
12283         }catch(e){
12284             this.fireEvent("loadexception", this, o, response, e);
12285             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12286             return;
12287         }
12288         
12289         this.fireEvent("load", this, o, o.request.arg);
12290         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12291     },
12292
12293     // private
12294     update : function(dataSet){
12295
12296     },
12297
12298     // private
12299     updateResponse : function(dataSet){
12300
12301     }
12302 });/*
12303  * Based on:
12304  * Ext JS Library 1.1.1
12305  * Copyright(c) 2006-2007, Ext JS, LLC.
12306  *
12307  * Originally Released Under LGPL - original licence link has changed is not relivant.
12308  *
12309  * Fork - LGPL
12310  * <script type="text/javascript">
12311  */
12312
12313 /**
12314  * @class Roo.data.ScriptTagProxy
12315  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12316  * other than the originating domain of the running page.<br><br>
12317  * <p>
12318  * <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
12319  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12320  * <p>
12321  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12322  * source code that is used as the source inside a &lt;script> tag.<br><br>
12323  * <p>
12324  * In order for the browser to process the returned data, the server must wrap the data object
12325  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12326  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12327  * depending on whether the callback name was passed:
12328  * <p>
12329  * <pre><code>
12330 boolean scriptTag = false;
12331 String cb = request.getParameter("callback");
12332 if (cb != null) {
12333     scriptTag = true;
12334     response.setContentType("text/javascript");
12335 } else {
12336     response.setContentType("application/x-json");
12337 }
12338 Writer out = response.getWriter();
12339 if (scriptTag) {
12340     out.write(cb + "(");
12341 }
12342 out.print(dataBlock.toJsonString());
12343 if (scriptTag) {
12344     out.write(");");
12345 }
12346 </pre></code>
12347  *
12348  * @constructor
12349  * @param {Object} config A configuration object.
12350  */
12351 Roo.data.ScriptTagProxy = function(config){
12352     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12353     Roo.apply(this, config);
12354     this.head = document.getElementsByTagName("head")[0];
12355 };
12356
12357 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12358
12359 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12360     /**
12361      * @cfg {String} url The URL from which to request the data object.
12362      */
12363     /**
12364      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12365      */
12366     timeout : 30000,
12367     /**
12368      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12369      * the server the name of the callback function set up by the load call to process the returned data object.
12370      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12371      * javascript output which calls this named function passing the data object as its only parameter.
12372      */
12373     callbackParam : "callback",
12374     /**
12375      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12376      * name to the request.
12377      */
12378     nocache : true,
12379
12380     /**
12381      * Load data from the configured URL, read the data object into
12382      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12383      * process that block using the passed callback.
12384      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12385      * for the request to the remote server.
12386      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12387      * object into a block of Roo.data.Records.
12388      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12389      * The function must be passed <ul>
12390      * <li>The Record block object</li>
12391      * <li>The "arg" argument from the load function</li>
12392      * <li>A boolean success indicator</li>
12393      * </ul>
12394      * @param {Object} scope The scope in which to call the callback
12395      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12396      */
12397     load : function(params, reader, callback, scope, arg){
12398         if(this.fireEvent("beforeload", this, params) !== false){
12399
12400             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12401
12402             var url = this.url;
12403             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12404             if(this.nocache){
12405                 url += "&_dc=" + (new Date().getTime());
12406             }
12407             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12408             var trans = {
12409                 id : transId,
12410                 cb : "stcCallback"+transId,
12411                 scriptId : "stcScript"+transId,
12412                 params : params,
12413                 arg : arg,
12414                 url : url,
12415                 callback : callback,
12416                 scope : scope,
12417                 reader : reader
12418             };
12419             var conn = this;
12420
12421             window[trans.cb] = function(o){
12422                 conn.handleResponse(o, trans);
12423             };
12424
12425             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12426
12427             if(this.autoAbort !== false){
12428                 this.abort();
12429             }
12430
12431             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12432
12433             var script = document.createElement("script");
12434             script.setAttribute("src", url);
12435             script.setAttribute("type", "text/javascript");
12436             script.setAttribute("id", trans.scriptId);
12437             this.head.appendChild(script);
12438
12439             this.trans = trans;
12440         }else{
12441             callback.call(scope||this, null, arg, false);
12442         }
12443     },
12444
12445     // private
12446     isLoading : function(){
12447         return this.trans ? true : false;
12448     },
12449
12450     /**
12451      * Abort the current server request.
12452      */
12453     abort : function(){
12454         if(this.isLoading()){
12455             this.destroyTrans(this.trans);
12456         }
12457     },
12458
12459     // private
12460     destroyTrans : function(trans, isLoaded){
12461         this.head.removeChild(document.getElementById(trans.scriptId));
12462         clearTimeout(trans.timeoutId);
12463         if(isLoaded){
12464             window[trans.cb] = undefined;
12465             try{
12466                 delete window[trans.cb];
12467             }catch(e){}
12468         }else{
12469             // if hasn't been loaded, wait for load to remove it to prevent script error
12470             window[trans.cb] = function(){
12471                 window[trans.cb] = undefined;
12472                 try{
12473                     delete window[trans.cb];
12474                 }catch(e){}
12475             };
12476         }
12477     },
12478
12479     // private
12480     handleResponse : function(o, trans){
12481         this.trans = false;
12482         this.destroyTrans(trans, true);
12483         var result;
12484         try {
12485             result = trans.reader.readRecords(o);
12486         }catch(e){
12487             this.fireEvent("loadexception", this, o, trans.arg, e);
12488             trans.callback.call(trans.scope||window, null, trans.arg, false);
12489             return;
12490         }
12491         this.fireEvent("load", this, o, trans.arg);
12492         trans.callback.call(trans.scope||window, result, trans.arg, true);
12493     },
12494
12495     // private
12496     handleFailure : function(trans){
12497         this.trans = false;
12498         this.destroyTrans(trans, false);
12499         this.fireEvent("loadexception", this, null, trans.arg);
12500         trans.callback.call(trans.scope||window, null, trans.arg, false);
12501     }
12502 });/*
12503  * Based on:
12504  * Ext JS Library 1.1.1
12505  * Copyright(c) 2006-2007, Ext JS, LLC.
12506  *
12507  * Originally Released Under LGPL - original licence link has changed is not relivant.
12508  *
12509  * Fork - LGPL
12510  * <script type="text/javascript">
12511  */
12512
12513 /**
12514  * @class Roo.data.JsonReader
12515  * @extends Roo.data.DataReader
12516  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12517  * based on mappings in a provided Roo.data.Record constructor.
12518  * 
12519  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12520  * in the reply previously. 
12521  * 
12522  * <p>
12523  * Example code:
12524  * <pre><code>
12525 var RecordDef = Roo.data.Record.create([
12526     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12527     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12528 ]);
12529 var myReader = new Roo.data.JsonReader({
12530     totalProperty: "results",    // The property which contains the total dataset size (optional)
12531     root: "rows",                // The property which contains an Array of row objects
12532     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12533 }, RecordDef);
12534 </code></pre>
12535  * <p>
12536  * This would consume a JSON file like this:
12537  * <pre><code>
12538 { 'results': 2, 'rows': [
12539     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12540     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12541 }
12542 </code></pre>
12543  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12544  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12545  * paged from the remote server.
12546  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12547  * @cfg {String} root name of the property which contains the Array of row objects.
12548  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12549  * @cfg {Array} fields Array of field definition objects
12550  * @constructor
12551  * Create a new JsonReader
12552  * @param {Object} meta Metadata configuration options
12553  * @param {Object} recordType Either an Array of field definition objects,
12554  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12555  */
12556 Roo.data.JsonReader = function(meta, recordType){
12557     
12558     meta = meta || {};
12559     // set some defaults:
12560     Roo.applyIf(meta, {
12561         totalProperty: 'total',
12562         successProperty : 'success',
12563         root : 'data',
12564         id : 'id'
12565     });
12566     
12567     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12568 };
12569 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12570     
12571     /**
12572      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12573      * Used by Store query builder to append _requestMeta to params.
12574      * 
12575      */
12576     metaFromRemote : false,
12577     /**
12578      * This method is only used by a DataProxy which has retrieved data from a remote server.
12579      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12580      * @return {Object} data A data block which is used by an Roo.data.Store object as
12581      * a cache of Roo.data.Records.
12582      */
12583     read : function(response){
12584         var json = response.responseText;
12585        
12586         var o = /* eval:var:o */ eval("("+json+")");
12587         if(!o) {
12588             throw {message: "JsonReader.read: Json object not found"};
12589         }
12590         
12591         if(o.metaData){
12592             
12593             delete this.ef;
12594             this.metaFromRemote = true;
12595             this.meta = o.metaData;
12596             this.recordType = Roo.data.Record.create(o.metaData.fields);
12597             this.onMetaChange(this.meta, this.recordType, o);
12598         }
12599         return this.readRecords(o);
12600     },
12601
12602     // private function a store will implement
12603     onMetaChange : function(meta, recordType, o){
12604
12605     },
12606
12607     /**
12608          * @ignore
12609          */
12610     simpleAccess: function(obj, subsc) {
12611         return obj[subsc];
12612     },
12613
12614         /**
12615          * @ignore
12616          */
12617     getJsonAccessor: function(){
12618         var re = /[\[\.]/;
12619         return function(expr) {
12620             try {
12621                 return(re.test(expr))
12622                     ? new Function("obj", "return obj." + expr)
12623                     : function(obj){
12624                         return obj[expr];
12625                     };
12626             } catch(e){}
12627             return Roo.emptyFn;
12628         };
12629     }(),
12630
12631     /**
12632      * Create a data block containing Roo.data.Records from an XML document.
12633      * @param {Object} o An object which contains an Array of row objects in the property specified
12634      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12635      * which contains the total size of the dataset.
12636      * @return {Object} data A data block which is used by an Roo.data.Store object as
12637      * a cache of Roo.data.Records.
12638      */
12639     readRecords : function(o){
12640         /**
12641          * After any data loads, the raw JSON data is available for further custom processing.
12642          * @type Object
12643          */
12644         this.o = o;
12645         var s = this.meta, Record = this.recordType,
12646             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12647
12648 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12649         if (!this.ef) {
12650             if(s.totalProperty) {
12651                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12652                 }
12653                 if(s.successProperty) {
12654                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12655                 }
12656                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12657                 if (s.id) {
12658                         var g = this.getJsonAccessor(s.id);
12659                         this.getId = function(rec) {
12660                                 var r = g(rec);  
12661                                 return (r === undefined || r === "") ? null : r;
12662                         };
12663                 } else {
12664                         this.getId = function(){return null;};
12665                 }
12666             this.ef = [];
12667             for(var jj = 0; jj < fl; jj++){
12668                 f = fi[jj];
12669                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12670                 this.ef[jj] = this.getJsonAccessor(map);
12671             }
12672         }
12673
12674         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12675         if(s.totalProperty){
12676             var vt = parseInt(this.getTotal(o), 10);
12677             if(!isNaN(vt)){
12678                 totalRecords = vt;
12679             }
12680         }
12681         if(s.successProperty){
12682             var vs = this.getSuccess(o);
12683             if(vs === false || vs === 'false'){
12684                 success = false;
12685             }
12686         }
12687         var records = [];
12688         for(var i = 0; i < c; i++){
12689                 var n = root[i];
12690             var values = {};
12691             var id = this.getId(n);
12692             for(var j = 0; j < fl; j++){
12693                 f = fi[j];
12694             var v = this.ef[j](n);
12695             if (!f.convert) {
12696                 Roo.log('missing convert for ' + f.name);
12697                 Roo.log(f);
12698                 continue;
12699             }
12700             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12701             }
12702             var record = new Record(values, id);
12703             record.json = n;
12704             records[i] = record;
12705         }
12706         return {
12707             raw : o,
12708             success : success,
12709             records : records,
12710             totalRecords : totalRecords
12711         };
12712     }
12713 });/*
12714  * Based on:
12715  * Ext JS Library 1.1.1
12716  * Copyright(c) 2006-2007, Ext JS, LLC.
12717  *
12718  * Originally Released Under LGPL - original licence link has changed is not relivant.
12719  *
12720  * Fork - LGPL
12721  * <script type="text/javascript">
12722  */
12723
12724 /**
12725  * @class Roo.data.ArrayReader
12726  * @extends Roo.data.DataReader
12727  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12728  * Each element of that Array represents a row of data fields. The
12729  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12730  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12731  * <p>
12732  * Example code:.
12733  * <pre><code>
12734 var RecordDef = Roo.data.Record.create([
12735     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12736     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12737 ]);
12738 var myReader = new Roo.data.ArrayReader({
12739     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12740 }, RecordDef);
12741 </code></pre>
12742  * <p>
12743  * This would consume an Array like this:
12744  * <pre><code>
12745 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12746   </code></pre>
12747  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12748  * @constructor
12749  * Create a new JsonReader
12750  * @param {Object} meta Metadata configuration options.
12751  * @param {Object} recordType Either an Array of field definition objects
12752  * as specified to {@link Roo.data.Record#create},
12753  * or an {@link Roo.data.Record} object
12754  * created using {@link Roo.data.Record#create}.
12755  */
12756 Roo.data.ArrayReader = function(meta, recordType){
12757     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12758 };
12759
12760 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12761     /**
12762      * Create a data block containing Roo.data.Records from an XML document.
12763      * @param {Object} o An Array of row objects which represents the dataset.
12764      * @return {Object} data A data block which is used by an Roo.data.Store object as
12765      * a cache of Roo.data.Records.
12766      */
12767     readRecords : function(o){
12768         var sid = this.meta ? this.meta.id : null;
12769         var recordType = this.recordType, fields = recordType.prototype.fields;
12770         var records = [];
12771         var root = o;
12772             for(var i = 0; i < root.length; i++){
12773                     var n = root[i];
12774                 var values = {};
12775                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12776                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12777                 var f = fields.items[j];
12778                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12779                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12780                 v = f.convert(v);
12781                 values[f.name] = v;
12782             }
12783                 var record = new recordType(values, id);
12784                 record.json = n;
12785                 records[records.length] = record;
12786             }
12787             return {
12788                 records : records,
12789                 totalRecords : records.length
12790             };
12791     }
12792 });/*
12793  * - LGPL
12794  * * 
12795  */
12796
12797 /**
12798  * @class Roo.bootstrap.ComboBox
12799  * @extends Roo.bootstrap.TriggerField
12800  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12801  * @cfg {Boolean} append (true|false) default false
12802  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12803  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12804  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12805  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12806  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12807  * @cfg {Boolean} animate default true
12808  * @cfg {Boolean} emptyResultText only for touch device
12809  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12810  * @cfg {String} emptyTitle default ''
12811  * @constructor
12812  * Create a new ComboBox.
12813  * @param {Object} config Configuration options
12814  */
12815 Roo.bootstrap.ComboBox = function(config){
12816     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12817     this.addEvents({
12818         /**
12819          * @event expand
12820          * Fires when the dropdown list is expanded
12821         * @param {Roo.bootstrap.ComboBox} combo This combo box
12822         */
12823         'expand' : true,
12824         /**
12825          * @event collapse
12826          * Fires when the dropdown list is collapsed
12827         * @param {Roo.bootstrap.ComboBox} combo This combo box
12828         */
12829         'collapse' : true,
12830         /**
12831          * @event beforeselect
12832          * Fires before a list item is selected. Return false to cancel the selection.
12833         * @param {Roo.bootstrap.ComboBox} combo This combo box
12834         * @param {Roo.data.Record} record The data record returned from the underlying store
12835         * @param {Number} index The index of the selected item in the dropdown list
12836         */
12837         'beforeselect' : true,
12838         /**
12839          * @event select
12840          * Fires when a list item is selected
12841         * @param {Roo.bootstrap.ComboBox} combo This combo box
12842         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12843         * @param {Number} index The index of the selected item in the dropdown list
12844         */
12845         'select' : true,
12846         /**
12847          * @event beforequery
12848          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12849          * The event object passed has these properties:
12850         * @param {Roo.bootstrap.ComboBox} combo This combo box
12851         * @param {String} query The query
12852         * @param {Boolean} forceAll true to force "all" query
12853         * @param {Boolean} cancel true to cancel the query
12854         * @param {Object} e The query event object
12855         */
12856         'beforequery': true,
12857          /**
12858          * @event add
12859          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12860         * @param {Roo.bootstrap.ComboBox} combo This combo box
12861         */
12862         'add' : true,
12863         /**
12864          * @event edit
12865          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12866         * @param {Roo.bootstrap.ComboBox} combo This combo box
12867         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12868         */
12869         'edit' : true,
12870         /**
12871          * @event remove
12872          * Fires when the remove value from the combobox array
12873         * @param {Roo.bootstrap.ComboBox} combo This combo box
12874         */
12875         'remove' : true,
12876         /**
12877          * @event afterremove
12878          * Fires when the remove value from the combobox array
12879         * @param {Roo.bootstrap.ComboBox} combo This combo box
12880         */
12881         'afterremove' : true,
12882         /**
12883          * @event specialfilter
12884          * Fires when specialfilter
12885             * @param {Roo.bootstrap.ComboBox} combo This combo box
12886             */
12887         'specialfilter' : true,
12888         /**
12889          * @event tick
12890          * Fires when tick the element
12891             * @param {Roo.bootstrap.ComboBox} combo This combo box
12892             */
12893         'tick' : true,
12894         /**
12895          * @event touchviewdisplay
12896          * Fires when touch view require special display (default is using displayField)
12897             * @param {Roo.bootstrap.ComboBox} combo This combo box
12898             * @param {Object} cfg set html .
12899             */
12900         'touchviewdisplay' : true
12901         
12902     });
12903     
12904     this.item = [];
12905     this.tickItems = [];
12906     
12907     this.selectedIndex = -1;
12908     if(this.mode == 'local'){
12909         if(config.queryDelay === undefined){
12910             this.queryDelay = 10;
12911         }
12912         if(config.minChars === undefined){
12913             this.minChars = 0;
12914         }
12915     }
12916 };
12917
12918 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12919      
12920     /**
12921      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12922      * rendering into an Roo.Editor, defaults to false)
12923      */
12924     /**
12925      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12926      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12927      */
12928     /**
12929      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12930      */
12931     /**
12932      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12933      * the dropdown list (defaults to undefined, with no header element)
12934      */
12935
12936      /**
12937      * @cfg {String/Roo.Template} tpl The template to use to render the output
12938      */
12939      
12940      /**
12941      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12942      */
12943     listWidth: undefined,
12944     /**
12945      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12946      * mode = 'remote' or 'text' if mode = 'local')
12947      */
12948     displayField: undefined,
12949     
12950     /**
12951      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12952      * mode = 'remote' or 'value' if mode = 'local'). 
12953      * Note: use of a valueField requires the user make a selection
12954      * in order for a value to be mapped.
12955      */
12956     valueField: undefined,
12957     /**
12958      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12959      */
12960     modalTitle : '',
12961     
12962     /**
12963      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12964      * field's data value (defaults to the underlying DOM element's name)
12965      */
12966     hiddenName: undefined,
12967     /**
12968      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12969      */
12970     listClass: '',
12971     /**
12972      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12973      */
12974     selectedClass: 'active',
12975     
12976     /**
12977      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12978      */
12979     shadow:'sides',
12980     /**
12981      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12982      * anchor positions (defaults to 'tl-bl')
12983      */
12984     listAlign: 'tl-bl?',
12985     /**
12986      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12987      */
12988     maxHeight: 300,
12989     /**
12990      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12991      * query specified by the allQuery config option (defaults to 'query')
12992      */
12993     triggerAction: 'query',
12994     /**
12995      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12996      * (defaults to 4, does not apply if editable = false)
12997      */
12998     minChars : 4,
12999     /**
13000      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13001      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13002      */
13003     typeAhead: false,
13004     /**
13005      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13006      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13007      */
13008     queryDelay: 500,
13009     /**
13010      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13011      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13012      */
13013     pageSize: 0,
13014     /**
13015      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13016      * when editable = true (defaults to false)
13017      */
13018     selectOnFocus:false,
13019     /**
13020      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13021      */
13022     queryParam: 'query',
13023     /**
13024      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13025      * when mode = 'remote' (defaults to 'Loading...')
13026      */
13027     loadingText: 'Loading...',
13028     /**
13029      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13030      */
13031     resizable: false,
13032     /**
13033      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13034      */
13035     handleHeight : 8,
13036     /**
13037      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13038      * traditional select (defaults to true)
13039      */
13040     editable: true,
13041     /**
13042      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13043      */
13044     allQuery: '',
13045     /**
13046      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13047      */
13048     mode: 'remote',
13049     /**
13050      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13051      * listWidth has a higher value)
13052      */
13053     minListWidth : 70,
13054     /**
13055      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13056      * allow the user to set arbitrary text into the field (defaults to false)
13057      */
13058     forceSelection:false,
13059     /**
13060      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13061      * if typeAhead = true (defaults to 250)
13062      */
13063     typeAheadDelay : 250,
13064     /**
13065      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13066      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13067      */
13068     valueNotFoundText : undefined,
13069     /**
13070      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13071      */
13072     blockFocus : false,
13073     
13074     /**
13075      * @cfg {Boolean} disableClear Disable showing of clear button.
13076      */
13077     disableClear : false,
13078     /**
13079      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13080      */
13081     alwaysQuery : false,
13082     
13083     /**
13084      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13085      */
13086     multiple : false,
13087     
13088     /**
13089      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13090      */
13091     invalidClass : "has-warning",
13092     
13093     /**
13094      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13095      */
13096     validClass : "has-success",
13097     
13098     /**
13099      * @cfg {Boolean} specialFilter (true|false) special filter default false
13100      */
13101     specialFilter : false,
13102     
13103     /**
13104      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13105      */
13106     mobileTouchView : true,
13107     
13108     /**
13109      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13110      */
13111     useNativeIOS : false,
13112     
13113     /**
13114      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13115      */
13116     mobile_restrict_height : false,
13117     
13118     ios_options : false,
13119     
13120     //private
13121     addicon : false,
13122     editicon: false,
13123     
13124     page: 0,
13125     hasQuery: false,
13126     append: false,
13127     loadNext: false,
13128     autoFocus : true,
13129     tickable : false,
13130     btnPosition : 'right',
13131     triggerList : true,
13132     showToggleBtn : true,
13133     animate : true,
13134     emptyResultText: 'Empty',
13135     triggerText : 'Select',
13136     emptyTitle : '',
13137     
13138     // element that contains real text value.. (when hidden is used..)
13139     
13140     getAutoCreate : function()
13141     {   
13142         var cfg = false;
13143         //render
13144         /*
13145          * Render classic select for iso
13146          */
13147         
13148         if(Roo.isIOS && this.useNativeIOS){
13149             cfg = this.getAutoCreateNativeIOS();
13150             return cfg;
13151         }
13152         
13153         /*
13154          * Touch Devices
13155          */
13156         
13157         if(Roo.isTouch && this.mobileTouchView){
13158             cfg = this.getAutoCreateTouchView();
13159             return cfg;;
13160         }
13161         
13162         /*
13163          *  Normal ComboBox
13164          */
13165         if(!this.tickable){
13166             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13167             return cfg;
13168         }
13169         
13170         /*
13171          *  ComboBox with tickable selections
13172          */
13173              
13174         var align = this.labelAlign || this.parentLabelAlign();
13175         
13176         cfg = {
13177             cls : 'form-group roo-combobox-tickable' //input-group
13178         };
13179         
13180         var btn_text_select = '';
13181         var btn_text_done = '';
13182         var btn_text_cancel = '';
13183         
13184         if (this.btn_text_show) {
13185             btn_text_select = 'Select';
13186             btn_text_done = 'Done';
13187             btn_text_cancel = 'Cancel'; 
13188         }
13189         
13190         var buttons = {
13191             tag : 'div',
13192             cls : 'tickable-buttons',
13193             cn : [
13194                 {
13195                     tag : 'button',
13196                     type : 'button',
13197                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13198                     //html : this.triggerText
13199                     html: btn_text_select
13200                 },
13201                 {
13202                     tag : 'button',
13203                     type : 'button',
13204                     name : 'ok',
13205                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13206                     //html : 'Done'
13207                     html: btn_text_done
13208                 },
13209                 {
13210                     tag : 'button',
13211                     type : 'button',
13212                     name : 'cancel',
13213                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13214                     //html : 'Cancel'
13215                     html: btn_text_cancel
13216                 }
13217             ]
13218         };
13219         
13220         if(this.editable){
13221             buttons.cn.unshift({
13222                 tag: 'input',
13223                 cls: 'roo-select2-search-field-input'
13224             });
13225         }
13226         
13227         var _this = this;
13228         
13229         Roo.each(buttons.cn, function(c){
13230             if (_this.size) {
13231                 c.cls += ' btn-' + _this.size;
13232             }
13233
13234             if (_this.disabled) {
13235                 c.disabled = true;
13236             }
13237         });
13238         
13239         var box = {
13240             tag: 'div',
13241             cn: [
13242                 {
13243                     tag: 'input',
13244                     type : 'hidden',
13245                     cls: 'form-hidden-field'
13246                 },
13247                 {
13248                     tag: 'ul',
13249                     cls: 'roo-select2-choices',
13250                     cn:[
13251                         {
13252                             tag: 'li',
13253                             cls: 'roo-select2-search-field',
13254                             cn: [
13255                                 buttons
13256                             ]
13257                         }
13258                     ]
13259                 }
13260             ]
13261         };
13262         
13263         var combobox = {
13264             cls: 'roo-select2-container input-group roo-select2-container-multi',
13265             cn: [
13266                 box
13267 //                {
13268 //                    tag: 'ul',
13269 //                    cls: 'typeahead typeahead-long dropdown-menu',
13270 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13271 //                }
13272             ]
13273         };
13274         
13275         if(this.hasFeedback && !this.allowBlank){
13276             
13277             var feedback = {
13278                 tag: 'span',
13279                 cls: 'glyphicon form-control-feedback'
13280             };
13281
13282             combobox.cn.push(feedback);
13283         }
13284         
13285         
13286         if (align ==='left' && this.fieldLabel.length) {
13287             
13288             cfg.cls += ' roo-form-group-label-left';
13289             
13290             cfg.cn = [
13291                 {
13292                     tag : 'i',
13293                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13294                     tooltip : 'This field is required'
13295                 },
13296                 {
13297                     tag: 'label',
13298                     'for' :  id,
13299                     cls : 'control-label',
13300                     html : this.fieldLabel
13301
13302                 },
13303                 {
13304                     cls : "", 
13305                     cn: [
13306                         combobox
13307                     ]
13308                 }
13309
13310             ];
13311             
13312             var labelCfg = cfg.cn[1];
13313             var contentCfg = cfg.cn[2];
13314             
13315
13316             if(this.indicatorpos == 'right'){
13317                 
13318                 cfg.cn = [
13319                     {
13320                         tag: 'label',
13321                         'for' :  id,
13322                         cls : 'control-label',
13323                         cn : [
13324                             {
13325                                 tag : 'span',
13326                                 html : this.fieldLabel
13327                             },
13328                             {
13329                                 tag : 'i',
13330                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13331                                 tooltip : 'This field is required'
13332                             }
13333                         ]
13334                     },
13335                     {
13336                         cls : "",
13337                         cn: [
13338                             combobox
13339                         ]
13340                     }
13341
13342                 ];
13343                 
13344                 
13345                 
13346                 labelCfg = cfg.cn[0];
13347                 contentCfg = cfg.cn[1];
13348             
13349             }
13350             
13351             if(this.labelWidth > 12){
13352                 labelCfg.style = "width: " + this.labelWidth + 'px';
13353             }
13354             
13355             if(this.labelWidth < 13 && this.labelmd == 0){
13356                 this.labelmd = this.labelWidth;
13357             }
13358             
13359             if(this.labellg > 0){
13360                 labelCfg.cls += ' col-lg-' + this.labellg;
13361                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13362             }
13363             
13364             if(this.labelmd > 0){
13365                 labelCfg.cls += ' col-md-' + this.labelmd;
13366                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13367             }
13368             
13369             if(this.labelsm > 0){
13370                 labelCfg.cls += ' col-sm-' + this.labelsm;
13371                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13372             }
13373             
13374             if(this.labelxs > 0){
13375                 labelCfg.cls += ' col-xs-' + this.labelxs;
13376                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13377             }
13378                 
13379                 
13380         } else if ( this.fieldLabel.length) {
13381 //                Roo.log(" label");
13382                  cfg.cn = [
13383                     {
13384                         tag : 'i',
13385                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13386                         tooltip : 'This field is required'
13387                     },
13388                     {
13389                         tag: 'label',
13390                         //cls : 'input-group-addon',
13391                         html : this.fieldLabel
13392                     },
13393                     combobox
13394                 ];
13395                 
13396                 if(this.indicatorpos == 'right'){
13397                     cfg.cn = [
13398                         {
13399                             tag: 'label',
13400                             //cls : 'input-group-addon',
13401                             html : this.fieldLabel
13402                         },
13403                         {
13404                             tag : 'i',
13405                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13406                             tooltip : 'This field is required'
13407                         },
13408                         combobox
13409                     ];
13410                     
13411                 }
13412
13413         } else {
13414             
13415 //                Roo.log(" no label && no align");
13416                 cfg = combobox
13417                      
13418                 
13419         }
13420          
13421         var settings=this;
13422         ['xs','sm','md','lg'].map(function(size){
13423             if (settings[size]) {
13424                 cfg.cls += ' col-' + size + '-' + settings[size];
13425             }
13426         });
13427         
13428         return cfg;
13429         
13430     },
13431     
13432     _initEventsCalled : false,
13433     
13434     // private
13435     initEvents: function()
13436     {   
13437         if (this._initEventsCalled) { // as we call render... prevent looping...
13438             return;
13439         }
13440         this._initEventsCalled = true;
13441         
13442         if (!this.store) {
13443             throw "can not find store for combo";
13444         }
13445         
13446         this.indicator = this.indicatorEl();
13447         
13448         this.store = Roo.factory(this.store, Roo.data);
13449         this.store.parent = this;
13450         
13451         // if we are building from html. then this element is so complex, that we can not really
13452         // use the rendered HTML.
13453         // so we have to trash and replace the previous code.
13454         if (Roo.XComponent.build_from_html) {
13455             // remove this element....
13456             var e = this.el.dom, k=0;
13457             while (e ) { e = e.previousSibling;  ++k;}
13458
13459             this.el.remove();
13460             
13461             this.el=false;
13462             this.rendered = false;
13463             
13464             this.render(this.parent().getChildContainer(true), k);
13465         }
13466         
13467         if(Roo.isIOS && this.useNativeIOS){
13468             this.initIOSView();
13469             return;
13470         }
13471         
13472         /*
13473          * Touch Devices
13474          */
13475         
13476         if(Roo.isTouch && this.mobileTouchView){
13477             this.initTouchView();
13478             return;
13479         }
13480         
13481         if(this.tickable){
13482             this.initTickableEvents();
13483             return;
13484         }
13485         
13486         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13487         
13488         if(this.hiddenName){
13489             
13490             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13491             
13492             this.hiddenField.dom.value =
13493                 this.hiddenValue !== undefined ? this.hiddenValue :
13494                 this.value !== undefined ? this.value : '';
13495
13496             // prevent input submission
13497             this.el.dom.removeAttribute('name');
13498             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13499              
13500              
13501         }
13502         //if(Roo.isGecko){
13503         //    this.el.dom.setAttribute('autocomplete', 'off');
13504         //}
13505         
13506         var cls = 'x-combo-list';
13507         
13508         //this.list = new Roo.Layer({
13509         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13510         //});
13511         
13512         var _this = this;
13513         
13514         (function(){
13515             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13516             _this.list.setWidth(lw);
13517         }).defer(100);
13518         
13519         this.list.on('mouseover', this.onViewOver, this);
13520         this.list.on('mousemove', this.onViewMove, this);
13521         this.list.on('scroll', this.onViewScroll, this);
13522         
13523         /*
13524         this.list.swallowEvent('mousewheel');
13525         this.assetHeight = 0;
13526
13527         if(this.title){
13528             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13529             this.assetHeight += this.header.getHeight();
13530         }
13531
13532         this.innerList = this.list.createChild({cls:cls+'-inner'});
13533         this.innerList.on('mouseover', this.onViewOver, this);
13534         this.innerList.on('mousemove', this.onViewMove, this);
13535         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13536         
13537         if(this.allowBlank && !this.pageSize && !this.disableClear){
13538             this.footer = this.list.createChild({cls:cls+'-ft'});
13539             this.pageTb = new Roo.Toolbar(this.footer);
13540            
13541         }
13542         if(this.pageSize){
13543             this.footer = this.list.createChild({cls:cls+'-ft'});
13544             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13545                     {pageSize: this.pageSize});
13546             
13547         }
13548         
13549         if (this.pageTb && this.allowBlank && !this.disableClear) {
13550             var _this = this;
13551             this.pageTb.add(new Roo.Toolbar.Fill(), {
13552                 cls: 'x-btn-icon x-btn-clear',
13553                 text: '&#160;',
13554                 handler: function()
13555                 {
13556                     _this.collapse();
13557                     _this.clearValue();
13558                     _this.onSelect(false, -1);
13559                 }
13560             });
13561         }
13562         if (this.footer) {
13563             this.assetHeight += this.footer.getHeight();
13564         }
13565         */
13566             
13567         if(!this.tpl){
13568             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13569         }
13570
13571         this.view = new Roo.View(this.list, this.tpl, {
13572             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13573         });
13574         //this.view.wrapEl.setDisplayed(false);
13575         this.view.on('click', this.onViewClick, this);
13576         
13577         
13578         this.store.on('beforeload', this.onBeforeLoad, this);
13579         this.store.on('load', this.onLoad, this);
13580         this.store.on('loadexception', this.onLoadException, this);
13581         /*
13582         if(this.resizable){
13583             this.resizer = new Roo.Resizable(this.list,  {
13584                pinned:true, handles:'se'
13585             });
13586             this.resizer.on('resize', function(r, w, h){
13587                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13588                 this.listWidth = w;
13589                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13590                 this.restrictHeight();
13591             }, this);
13592             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13593         }
13594         */
13595         if(!this.editable){
13596             this.editable = true;
13597             this.setEditable(false);
13598         }
13599         
13600         /*
13601         
13602         if (typeof(this.events.add.listeners) != 'undefined') {
13603             
13604             this.addicon = this.wrap.createChild(
13605                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13606        
13607             this.addicon.on('click', function(e) {
13608                 this.fireEvent('add', this);
13609             }, this);
13610         }
13611         if (typeof(this.events.edit.listeners) != 'undefined') {
13612             
13613             this.editicon = this.wrap.createChild(
13614                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13615             if (this.addicon) {
13616                 this.editicon.setStyle('margin-left', '40px');
13617             }
13618             this.editicon.on('click', function(e) {
13619                 
13620                 // we fire even  if inothing is selected..
13621                 this.fireEvent('edit', this, this.lastData );
13622                 
13623             }, this);
13624         }
13625         */
13626         
13627         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13628             "up" : function(e){
13629                 this.inKeyMode = true;
13630                 this.selectPrev();
13631             },
13632
13633             "down" : function(e){
13634                 if(!this.isExpanded()){
13635                     this.onTriggerClick();
13636                 }else{
13637                     this.inKeyMode = true;
13638                     this.selectNext();
13639                 }
13640             },
13641
13642             "enter" : function(e){
13643 //                this.onViewClick();
13644                 //return true;
13645                 this.collapse();
13646                 
13647                 if(this.fireEvent("specialkey", this, e)){
13648                     this.onViewClick(false);
13649                 }
13650                 
13651                 return true;
13652             },
13653
13654             "esc" : function(e){
13655                 this.collapse();
13656             },
13657
13658             "tab" : function(e){
13659                 this.collapse();
13660                 
13661                 if(this.fireEvent("specialkey", this, e)){
13662                     this.onViewClick(false);
13663                 }
13664                 
13665                 return true;
13666             },
13667
13668             scope : this,
13669
13670             doRelay : function(foo, bar, hname){
13671                 if(hname == 'down' || this.scope.isExpanded()){
13672                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13673                 }
13674                 return true;
13675             },
13676
13677             forceKeyDown: true
13678         });
13679         
13680         
13681         this.queryDelay = Math.max(this.queryDelay || 10,
13682                 this.mode == 'local' ? 10 : 250);
13683         
13684         
13685         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13686         
13687         if(this.typeAhead){
13688             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13689         }
13690         if(this.editable !== false){
13691             this.inputEl().on("keyup", this.onKeyUp, this);
13692         }
13693         if(this.forceSelection){
13694             this.inputEl().on('blur', this.doForce, this);
13695         }
13696         
13697         if(this.multiple){
13698             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13699             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13700         }
13701     },
13702     
13703     initTickableEvents: function()
13704     {   
13705         this.createList();
13706         
13707         if(this.hiddenName){
13708             
13709             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13710             
13711             this.hiddenField.dom.value =
13712                 this.hiddenValue !== undefined ? this.hiddenValue :
13713                 this.value !== undefined ? this.value : '';
13714
13715             // prevent input submission
13716             this.el.dom.removeAttribute('name');
13717             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13718              
13719              
13720         }
13721         
13722 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13723         
13724         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13725         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13726         if(this.triggerList){
13727             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13728         }
13729          
13730         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13731         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13732         
13733         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13734         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13735         
13736         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13737         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13738         
13739         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13740         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13741         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13742         
13743         this.okBtn.hide();
13744         this.cancelBtn.hide();
13745         
13746         var _this = this;
13747         
13748         (function(){
13749             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13750             _this.list.setWidth(lw);
13751         }).defer(100);
13752         
13753         this.list.on('mouseover', this.onViewOver, this);
13754         this.list.on('mousemove', this.onViewMove, this);
13755         
13756         this.list.on('scroll', this.onViewScroll, this);
13757         
13758         if(!this.tpl){
13759             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13760                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13761         }
13762
13763         this.view = new Roo.View(this.list, this.tpl, {
13764             singleSelect:true,
13765             tickable:true,
13766             parent:this,
13767             store: this.store,
13768             selectedClass: this.selectedClass
13769         });
13770         
13771         //this.view.wrapEl.setDisplayed(false);
13772         this.view.on('click', this.onViewClick, this);
13773         
13774         
13775         
13776         this.store.on('beforeload', this.onBeforeLoad, this);
13777         this.store.on('load', this.onLoad, this);
13778         this.store.on('loadexception', this.onLoadException, this);
13779         
13780         if(this.editable){
13781             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13782                 "up" : function(e){
13783                     this.inKeyMode = true;
13784                     this.selectPrev();
13785                 },
13786
13787                 "down" : function(e){
13788                     this.inKeyMode = true;
13789                     this.selectNext();
13790                 },
13791
13792                 "enter" : function(e){
13793                     if(this.fireEvent("specialkey", this, e)){
13794                         this.onViewClick(false);
13795                     }
13796                     
13797                     return true;
13798                 },
13799
13800                 "esc" : function(e){
13801                     this.onTickableFooterButtonClick(e, false, false);
13802                 },
13803
13804                 "tab" : function(e){
13805                     this.fireEvent("specialkey", this, e);
13806                     
13807                     this.onTickableFooterButtonClick(e, false, false);
13808                     
13809                     return true;
13810                 },
13811
13812                 scope : this,
13813
13814                 doRelay : function(e, fn, key){
13815                     if(this.scope.isExpanded()){
13816                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13817                     }
13818                     return true;
13819                 },
13820
13821                 forceKeyDown: true
13822             });
13823         }
13824         
13825         this.queryDelay = Math.max(this.queryDelay || 10,
13826                 this.mode == 'local' ? 10 : 250);
13827         
13828         
13829         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13830         
13831         if(this.typeAhead){
13832             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13833         }
13834         
13835         if(this.editable !== false){
13836             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13837         }
13838         
13839         this.indicator = this.indicatorEl();
13840         
13841         if(this.indicator){
13842             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13843             this.indicator.hide();
13844         }
13845         
13846     },
13847
13848     onDestroy : function(){
13849         if(this.view){
13850             this.view.setStore(null);
13851             this.view.el.removeAllListeners();
13852             this.view.el.remove();
13853             this.view.purgeListeners();
13854         }
13855         if(this.list){
13856             this.list.dom.innerHTML  = '';
13857         }
13858         
13859         if(this.store){
13860             this.store.un('beforeload', this.onBeforeLoad, this);
13861             this.store.un('load', this.onLoad, this);
13862             this.store.un('loadexception', this.onLoadException, this);
13863         }
13864         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13865     },
13866
13867     // private
13868     fireKey : function(e){
13869         if(e.isNavKeyPress() && !this.list.isVisible()){
13870             this.fireEvent("specialkey", this, e);
13871         }
13872     },
13873
13874     // private
13875     onResize: function(w, h){
13876 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13877 //        
13878 //        if(typeof w != 'number'){
13879 //            // we do not handle it!?!?
13880 //            return;
13881 //        }
13882 //        var tw = this.trigger.getWidth();
13883 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13884 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13885 //        var x = w - tw;
13886 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13887 //            
13888 //        //this.trigger.setStyle('left', x+'px');
13889 //        
13890 //        if(this.list && this.listWidth === undefined){
13891 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13892 //            this.list.setWidth(lw);
13893 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13894 //        }
13895         
13896     
13897         
13898     },
13899
13900     /**
13901      * Allow or prevent the user from directly editing the field text.  If false is passed,
13902      * the user will only be able to select from the items defined in the dropdown list.  This method
13903      * is the runtime equivalent of setting the 'editable' config option at config time.
13904      * @param {Boolean} value True to allow the user to directly edit the field text
13905      */
13906     setEditable : function(value){
13907         if(value == this.editable){
13908             return;
13909         }
13910         this.editable = value;
13911         if(!value){
13912             this.inputEl().dom.setAttribute('readOnly', true);
13913             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13914             this.inputEl().addClass('x-combo-noedit');
13915         }else{
13916             this.inputEl().dom.setAttribute('readOnly', false);
13917             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13918             this.inputEl().removeClass('x-combo-noedit');
13919         }
13920     },
13921
13922     // private
13923     
13924     onBeforeLoad : function(combo,opts){
13925         if(!this.hasFocus){
13926             return;
13927         }
13928          if (!opts.add) {
13929             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13930          }
13931         this.restrictHeight();
13932         this.selectedIndex = -1;
13933     },
13934
13935     // private
13936     onLoad : function(){
13937         
13938         this.hasQuery = false;
13939         
13940         if(!this.hasFocus){
13941             return;
13942         }
13943         
13944         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13945             this.loading.hide();
13946         }
13947         
13948         if(this.store.getCount() > 0){
13949             
13950             this.expand();
13951             this.restrictHeight();
13952             if(this.lastQuery == this.allQuery){
13953                 if(this.editable && !this.tickable){
13954                     this.inputEl().dom.select();
13955                 }
13956                 
13957                 if(
13958                     !this.selectByValue(this.value, true) &&
13959                     this.autoFocus && 
13960                     (
13961                         !this.store.lastOptions ||
13962                         typeof(this.store.lastOptions.add) == 'undefined' || 
13963                         this.store.lastOptions.add != true
13964                     )
13965                 ){
13966                     this.select(0, true);
13967                 }
13968             }else{
13969                 if(this.autoFocus){
13970                     this.selectNext();
13971                 }
13972                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13973                     this.taTask.delay(this.typeAheadDelay);
13974                 }
13975             }
13976         }else{
13977             this.onEmptyResults();
13978         }
13979         
13980         //this.el.focus();
13981     },
13982     // private
13983     onLoadException : function()
13984     {
13985         this.hasQuery = false;
13986         
13987         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13988             this.loading.hide();
13989         }
13990         
13991         if(this.tickable && this.editable){
13992             return;
13993         }
13994         
13995         this.collapse();
13996         // only causes errors at present
13997         //Roo.log(this.store.reader.jsonData);
13998         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13999             // fixme
14000             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14001         //}
14002         
14003         
14004     },
14005     // private
14006     onTypeAhead : function(){
14007         if(this.store.getCount() > 0){
14008             var r = this.store.getAt(0);
14009             var newValue = r.data[this.displayField];
14010             var len = newValue.length;
14011             var selStart = this.getRawValue().length;
14012             
14013             if(selStart != len){
14014                 this.setRawValue(newValue);
14015                 this.selectText(selStart, newValue.length);
14016             }
14017         }
14018     },
14019
14020     // private
14021     onSelect : function(record, index){
14022         
14023         if(this.fireEvent('beforeselect', this, record, index) !== false){
14024         
14025             this.setFromData(index > -1 ? record.data : false);
14026             
14027             this.collapse();
14028             this.fireEvent('select', this, record, index);
14029         }
14030     },
14031
14032     /**
14033      * Returns the currently selected field value or empty string if no value is set.
14034      * @return {String} value The selected value
14035      */
14036     getValue : function()
14037     {
14038         if(Roo.isIOS && this.useNativeIOS){
14039             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14040         }
14041         
14042         if(this.multiple){
14043             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14044         }
14045         
14046         if(this.valueField){
14047             return typeof this.value != 'undefined' ? this.value : '';
14048         }else{
14049             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14050         }
14051     },
14052     
14053     getRawValue : function()
14054     {
14055         if(Roo.isIOS && this.useNativeIOS){
14056             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14057         }
14058         
14059         var v = this.inputEl().getValue();
14060         
14061         return v;
14062     },
14063
14064     /**
14065      * Clears any text/value currently set in the field
14066      */
14067     clearValue : function(){
14068         
14069         if(this.hiddenField){
14070             this.hiddenField.dom.value = '';
14071         }
14072         this.value = '';
14073         this.setRawValue('');
14074         this.lastSelectionText = '';
14075         this.lastData = false;
14076         
14077         var close = this.closeTriggerEl();
14078         
14079         if(close){
14080             close.hide();
14081         }
14082         
14083         this.validate();
14084         
14085     },
14086
14087     /**
14088      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14089      * will be displayed in the field.  If the value does not match the data value of an existing item,
14090      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14091      * Otherwise the field will be blank (although the value will still be set).
14092      * @param {String} value The value to match
14093      */
14094     setValue : function(v)
14095     {
14096         if(Roo.isIOS && this.useNativeIOS){
14097             this.setIOSValue(v);
14098             return;
14099         }
14100         
14101         if(this.multiple){
14102             this.syncValue();
14103             return;
14104         }
14105         
14106         var text = v;
14107         if(this.valueField){
14108             var r = this.findRecord(this.valueField, v);
14109             if(r){
14110                 text = r.data[this.displayField];
14111             }else if(this.valueNotFoundText !== undefined){
14112                 text = this.valueNotFoundText;
14113             }
14114         }
14115         this.lastSelectionText = text;
14116         if(this.hiddenField){
14117             this.hiddenField.dom.value = v;
14118         }
14119         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14120         this.value = v;
14121         
14122         var close = this.closeTriggerEl();
14123         
14124         if(close){
14125             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14126         }
14127         
14128         this.validate();
14129     },
14130     /**
14131      * @property {Object} the last set data for the element
14132      */
14133     
14134     lastData : false,
14135     /**
14136      * Sets the value of the field based on a object which is related to the record format for the store.
14137      * @param {Object} value the value to set as. or false on reset?
14138      */
14139     setFromData : function(o){
14140         
14141         if(this.multiple){
14142             this.addItem(o);
14143             return;
14144         }
14145             
14146         var dv = ''; // display value
14147         var vv = ''; // value value..
14148         this.lastData = o;
14149         if (this.displayField) {
14150             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14151         } else {
14152             // this is an error condition!!!
14153             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14154         }
14155         
14156         if(this.valueField){
14157             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14158         }
14159         
14160         var close = this.closeTriggerEl();
14161         
14162         if(close){
14163             if(dv.length || vv * 1 > 0){
14164                 close.show() ;
14165                 this.blockFocus=true;
14166             } else {
14167                 close.hide();
14168             }             
14169         }
14170         
14171         if(this.hiddenField){
14172             this.hiddenField.dom.value = vv;
14173             
14174             this.lastSelectionText = dv;
14175             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14176             this.value = vv;
14177             return;
14178         }
14179         // no hidden field.. - we store the value in 'value', but still display
14180         // display field!!!!
14181         this.lastSelectionText = dv;
14182         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14183         this.value = vv;
14184         
14185         
14186         
14187     },
14188     // private
14189     reset : function(){
14190         // overridden so that last data is reset..
14191         
14192         if(this.multiple){
14193             this.clearItem();
14194             return;
14195         }
14196         
14197         this.setValue(this.originalValue);
14198         //this.clearInvalid();
14199         this.lastData = false;
14200         if (this.view) {
14201             this.view.clearSelections();
14202         }
14203         
14204         this.validate();
14205     },
14206     // private
14207     findRecord : function(prop, value){
14208         var record;
14209         if(this.store.getCount() > 0){
14210             this.store.each(function(r){
14211                 if(r.data[prop] == value){
14212                     record = r;
14213                     return false;
14214                 }
14215                 return true;
14216             });
14217         }
14218         return record;
14219     },
14220     
14221     getName: function()
14222     {
14223         // returns hidden if it's set..
14224         if (!this.rendered) {return ''};
14225         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14226         
14227     },
14228     // private
14229     onViewMove : function(e, t){
14230         this.inKeyMode = false;
14231     },
14232
14233     // private
14234     onViewOver : function(e, t){
14235         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14236             return;
14237         }
14238         var item = this.view.findItemFromChild(t);
14239         
14240         if(item){
14241             var index = this.view.indexOf(item);
14242             this.select(index, false);
14243         }
14244     },
14245
14246     // private
14247     onViewClick : function(view, doFocus, el, e)
14248     {
14249         var index = this.view.getSelectedIndexes()[0];
14250         
14251         var r = this.store.getAt(index);
14252         
14253         if(this.tickable){
14254             
14255             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14256                 return;
14257             }
14258             
14259             var rm = false;
14260             var _this = this;
14261             
14262             Roo.each(this.tickItems, function(v,k){
14263                 
14264                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14265                     Roo.log(v);
14266                     _this.tickItems.splice(k, 1);
14267                     
14268                     if(typeof(e) == 'undefined' && view == false){
14269                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14270                     }
14271                     
14272                     rm = true;
14273                     return;
14274                 }
14275             });
14276             
14277             if(rm){
14278                 return;
14279             }
14280             
14281             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14282                 this.tickItems.push(r.data);
14283             }
14284             
14285             if(typeof(e) == 'undefined' && view == false){
14286                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14287             }
14288                     
14289             return;
14290         }
14291         
14292         if(r){
14293             this.onSelect(r, index);
14294         }
14295         if(doFocus !== false && !this.blockFocus){
14296             this.inputEl().focus();
14297         }
14298     },
14299
14300     // private
14301     restrictHeight : function(){
14302         //this.innerList.dom.style.height = '';
14303         //var inner = this.innerList.dom;
14304         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14305         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14306         //this.list.beginUpdate();
14307         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14308         this.list.alignTo(this.inputEl(), this.listAlign);
14309         this.list.alignTo(this.inputEl(), this.listAlign);
14310         //this.list.endUpdate();
14311     },
14312
14313     // private
14314     onEmptyResults : function(){
14315         
14316         if(this.tickable && this.editable){
14317             this.hasFocus = false;
14318             this.restrictHeight();
14319             return;
14320         }
14321         
14322         this.collapse();
14323     },
14324
14325     /**
14326      * Returns true if the dropdown list is expanded, else false.
14327      */
14328     isExpanded : function(){
14329         return this.list.isVisible();
14330     },
14331
14332     /**
14333      * Select an item in the dropdown list by its data value. 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 {String} value The data value of the 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      * @return {Boolean} True if the value matched an item in the list, else false
14339      */
14340     selectByValue : function(v, scrollIntoView){
14341         if(v !== undefined && v !== null){
14342             var r = this.findRecord(this.valueField || this.displayField, v);
14343             if(r){
14344                 this.select(this.store.indexOf(r), scrollIntoView);
14345                 return true;
14346             }
14347         }
14348         return false;
14349     },
14350
14351     /**
14352      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14353      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14354      * @param {Number} index The zero-based index of the list item to select
14355      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14356      * selected item if it is not currently in view (defaults to true)
14357      */
14358     select : function(index, scrollIntoView){
14359         this.selectedIndex = index;
14360         this.view.select(index);
14361         if(scrollIntoView !== false){
14362             var el = this.view.getNode(index);
14363             /*
14364              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14365              */
14366             if(el){
14367                 this.list.scrollChildIntoView(el, false);
14368             }
14369         }
14370     },
14371
14372     // private
14373     selectNext : function(){
14374         var ct = this.store.getCount();
14375         if(ct > 0){
14376             if(this.selectedIndex == -1){
14377                 this.select(0);
14378             }else if(this.selectedIndex < ct-1){
14379                 this.select(this.selectedIndex+1);
14380             }
14381         }
14382     },
14383
14384     // private
14385     selectPrev : function(){
14386         var ct = this.store.getCount();
14387         if(ct > 0){
14388             if(this.selectedIndex == -1){
14389                 this.select(0);
14390             }else if(this.selectedIndex != 0){
14391                 this.select(this.selectedIndex-1);
14392             }
14393         }
14394     },
14395
14396     // private
14397     onKeyUp : function(e){
14398         if(this.editable !== false && !e.isSpecialKey()){
14399             this.lastKey = e.getKey();
14400             this.dqTask.delay(this.queryDelay);
14401         }
14402     },
14403
14404     // private
14405     validateBlur : function(){
14406         return !this.list || !this.list.isVisible();   
14407     },
14408
14409     // private
14410     initQuery : function(){
14411         
14412         var v = this.getRawValue();
14413         
14414         if(this.tickable && this.editable){
14415             v = this.tickableInputEl().getValue();
14416         }
14417         
14418         this.doQuery(v);
14419     },
14420
14421     // private
14422     doForce : function(){
14423         if(this.inputEl().dom.value.length > 0){
14424             this.inputEl().dom.value =
14425                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14426              
14427         }
14428     },
14429
14430     /**
14431      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14432      * query allowing the query action to be canceled if needed.
14433      * @param {String} query The SQL query to execute
14434      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14435      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14436      * saved in the current store (defaults to false)
14437      */
14438     doQuery : function(q, forceAll){
14439         
14440         if(q === undefined || q === null){
14441             q = '';
14442         }
14443         var qe = {
14444             query: q,
14445             forceAll: forceAll,
14446             combo: this,
14447             cancel:false
14448         };
14449         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14450             return false;
14451         }
14452         q = qe.query;
14453         
14454         forceAll = qe.forceAll;
14455         if(forceAll === true || (q.length >= this.minChars)){
14456             
14457             this.hasQuery = true;
14458             
14459             if(this.lastQuery != q || this.alwaysQuery){
14460                 this.lastQuery = q;
14461                 if(this.mode == 'local'){
14462                     this.selectedIndex = -1;
14463                     if(forceAll){
14464                         this.store.clearFilter();
14465                     }else{
14466                         
14467                         if(this.specialFilter){
14468                             this.fireEvent('specialfilter', this);
14469                             this.onLoad();
14470                             return;
14471                         }
14472                         
14473                         this.store.filter(this.displayField, q);
14474                     }
14475                     
14476                     this.store.fireEvent("datachanged", this.store);
14477                     
14478                     this.onLoad();
14479                     
14480                     
14481                 }else{
14482                     
14483                     this.store.baseParams[this.queryParam] = q;
14484                     
14485                     var options = {params : this.getParams(q)};
14486                     
14487                     if(this.loadNext){
14488                         options.add = true;
14489                         options.params.start = this.page * this.pageSize;
14490                     }
14491                     
14492                     this.store.load(options);
14493                     
14494                     /*
14495                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14496                      *  we should expand the list on onLoad
14497                      *  so command out it
14498                      */
14499 //                    this.expand();
14500                 }
14501             }else{
14502                 this.selectedIndex = -1;
14503                 this.onLoad();   
14504             }
14505         }
14506         
14507         this.loadNext = false;
14508     },
14509     
14510     // private
14511     getParams : function(q){
14512         var p = {};
14513         //p[this.queryParam] = q;
14514         
14515         if(this.pageSize){
14516             p.start = 0;
14517             p.limit = this.pageSize;
14518         }
14519         return p;
14520     },
14521
14522     /**
14523      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14524      */
14525     collapse : function(){
14526         if(!this.isExpanded()){
14527             return;
14528         }
14529         
14530         this.list.hide();
14531         
14532         this.hasFocus = false;
14533         
14534         if(this.tickable){
14535             this.okBtn.hide();
14536             this.cancelBtn.hide();
14537             this.trigger.show();
14538             
14539             if(this.editable){
14540                 this.tickableInputEl().dom.value = '';
14541                 this.tickableInputEl().blur();
14542             }
14543             
14544         }
14545         
14546         Roo.get(document).un('mousedown', this.collapseIf, this);
14547         Roo.get(document).un('mousewheel', this.collapseIf, this);
14548         if (!this.editable) {
14549             Roo.get(document).un('keydown', this.listKeyPress, this);
14550         }
14551         this.fireEvent('collapse', this);
14552         
14553         this.validate();
14554     },
14555
14556     // private
14557     collapseIf : function(e){
14558         var in_combo  = e.within(this.el);
14559         var in_list =  e.within(this.list);
14560         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14561         
14562         if (in_combo || in_list || is_list) {
14563             //e.stopPropagation();
14564             return;
14565         }
14566         
14567         if(this.tickable){
14568             this.onTickableFooterButtonClick(e, false, false);
14569         }
14570
14571         this.collapse();
14572         
14573     },
14574
14575     /**
14576      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14577      */
14578     expand : function(){
14579        
14580         if(this.isExpanded() || !this.hasFocus){
14581             return;
14582         }
14583         
14584         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14585         this.list.setWidth(lw);
14586         
14587         Roo.log('expand');
14588         
14589         this.list.show();
14590         
14591         this.restrictHeight();
14592         
14593         if(this.tickable){
14594             
14595             this.tickItems = Roo.apply([], this.item);
14596             
14597             this.okBtn.show();
14598             this.cancelBtn.show();
14599             this.trigger.hide();
14600             
14601             if(this.editable){
14602                 this.tickableInputEl().focus();
14603             }
14604             
14605         }
14606         
14607         Roo.get(document).on('mousedown', this.collapseIf, this);
14608         Roo.get(document).on('mousewheel', this.collapseIf, this);
14609         if (!this.editable) {
14610             Roo.get(document).on('keydown', this.listKeyPress, this);
14611         }
14612         
14613         this.fireEvent('expand', this);
14614     },
14615
14616     // private
14617     // Implements the default empty TriggerField.onTriggerClick function
14618     onTriggerClick : function(e)
14619     {
14620         Roo.log('trigger click');
14621         
14622         if(this.disabled || !this.triggerList){
14623             return;
14624         }
14625         
14626         this.page = 0;
14627         this.loadNext = false;
14628         
14629         if(this.isExpanded()){
14630             this.collapse();
14631             if (!this.blockFocus) {
14632                 this.inputEl().focus();
14633             }
14634             
14635         }else {
14636             this.hasFocus = true;
14637             if(this.triggerAction == 'all') {
14638                 this.doQuery(this.allQuery, true);
14639             } else {
14640                 this.doQuery(this.getRawValue());
14641             }
14642             if (!this.blockFocus) {
14643                 this.inputEl().focus();
14644             }
14645         }
14646     },
14647     
14648     onTickableTriggerClick : function(e)
14649     {
14650         if(this.disabled){
14651             return;
14652         }
14653         
14654         this.page = 0;
14655         this.loadNext = false;
14656         this.hasFocus = true;
14657         
14658         if(this.triggerAction == 'all') {
14659             this.doQuery(this.allQuery, true);
14660         } else {
14661             this.doQuery(this.getRawValue());
14662         }
14663     },
14664     
14665     onSearchFieldClick : function(e)
14666     {
14667         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14668             this.onTickableFooterButtonClick(e, false, false);
14669             return;
14670         }
14671         
14672         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14673             return;
14674         }
14675         
14676         this.page = 0;
14677         this.loadNext = false;
14678         this.hasFocus = true;
14679         
14680         if(this.triggerAction == 'all') {
14681             this.doQuery(this.allQuery, true);
14682         } else {
14683             this.doQuery(this.getRawValue());
14684         }
14685     },
14686     
14687     listKeyPress : function(e)
14688     {
14689         //Roo.log('listkeypress');
14690         // scroll to first matching element based on key pres..
14691         if (e.isSpecialKey()) {
14692             return false;
14693         }
14694         var k = String.fromCharCode(e.getKey()).toUpperCase();
14695         //Roo.log(k);
14696         var match  = false;
14697         var csel = this.view.getSelectedNodes();
14698         var cselitem = false;
14699         if (csel.length) {
14700             var ix = this.view.indexOf(csel[0]);
14701             cselitem  = this.store.getAt(ix);
14702             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14703                 cselitem = false;
14704             }
14705             
14706         }
14707         
14708         this.store.each(function(v) { 
14709             if (cselitem) {
14710                 // start at existing selection.
14711                 if (cselitem.id == v.id) {
14712                     cselitem = false;
14713                 }
14714                 return true;
14715             }
14716                 
14717             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14718                 match = this.store.indexOf(v);
14719                 return false;
14720             }
14721             return true;
14722         }, this);
14723         
14724         if (match === false) {
14725             return true; // no more action?
14726         }
14727         // scroll to?
14728         this.view.select(match);
14729         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14730         sn.scrollIntoView(sn.dom.parentNode, false);
14731     },
14732     
14733     onViewScroll : function(e, t){
14734         
14735         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){
14736             return;
14737         }
14738         
14739         this.hasQuery = true;
14740         
14741         this.loading = this.list.select('.loading', true).first();
14742         
14743         if(this.loading === null){
14744             this.list.createChild({
14745                 tag: 'div',
14746                 cls: 'loading roo-select2-more-results roo-select2-active',
14747                 html: 'Loading more results...'
14748             });
14749             
14750             this.loading = this.list.select('.loading', true).first();
14751             
14752             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14753             
14754             this.loading.hide();
14755         }
14756         
14757         this.loading.show();
14758         
14759         var _combo = this;
14760         
14761         this.page++;
14762         this.loadNext = true;
14763         
14764         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14765         
14766         return;
14767     },
14768     
14769     addItem : function(o)
14770     {   
14771         var dv = ''; // display value
14772         
14773         if (this.displayField) {
14774             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14775         } else {
14776             // this is an error condition!!!
14777             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14778         }
14779         
14780         if(!dv.length){
14781             return;
14782         }
14783         
14784         var choice = this.choices.createChild({
14785             tag: 'li',
14786             cls: 'roo-select2-search-choice',
14787             cn: [
14788                 {
14789                     tag: 'div',
14790                     html: dv
14791                 },
14792                 {
14793                     tag: 'a',
14794                     href: '#',
14795                     cls: 'roo-select2-search-choice-close fa fa-times',
14796                     tabindex: '-1'
14797                 }
14798             ]
14799             
14800         }, this.searchField);
14801         
14802         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14803         
14804         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14805         
14806         this.item.push(o);
14807         
14808         this.lastData = o;
14809         
14810         this.syncValue();
14811         
14812         this.inputEl().dom.value = '';
14813         
14814         this.validate();
14815     },
14816     
14817     onRemoveItem : function(e, _self, o)
14818     {
14819         e.preventDefault();
14820         
14821         this.lastItem = Roo.apply([], this.item);
14822         
14823         var index = this.item.indexOf(o.data) * 1;
14824         
14825         if( index < 0){
14826             Roo.log('not this item?!');
14827             return;
14828         }
14829         
14830         this.item.splice(index, 1);
14831         o.item.remove();
14832         
14833         this.syncValue();
14834         
14835         this.fireEvent('remove', this, e);
14836         
14837         this.validate();
14838         
14839     },
14840     
14841     syncValue : function()
14842     {
14843         if(!this.item.length){
14844             this.clearValue();
14845             return;
14846         }
14847             
14848         var value = [];
14849         var _this = this;
14850         Roo.each(this.item, function(i){
14851             if(_this.valueField){
14852                 value.push(i[_this.valueField]);
14853                 return;
14854             }
14855
14856             value.push(i);
14857         });
14858
14859         this.value = value.join(',');
14860
14861         if(this.hiddenField){
14862             this.hiddenField.dom.value = this.value;
14863         }
14864         
14865         this.store.fireEvent("datachanged", this.store);
14866         
14867         this.validate();
14868     },
14869     
14870     clearItem : function()
14871     {
14872         if(!this.multiple){
14873             return;
14874         }
14875         
14876         this.item = [];
14877         
14878         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14879            c.remove();
14880         });
14881         
14882         this.syncValue();
14883         
14884         this.validate();
14885         
14886         if(this.tickable && !Roo.isTouch){
14887             this.view.refresh();
14888         }
14889     },
14890     
14891     inputEl: function ()
14892     {
14893         if(Roo.isIOS && this.useNativeIOS){
14894             return this.el.select('select.roo-ios-select', true).first();
14895         }
14896         
14897         if(Roo.isTouch && this.mobileTouchView){
14898             return this.el.select('input.form-control',true).first();
14899         }
14900         
14901         if(this.tickable){
14902             return this.searchField;
14903         }
14904         
14905         return this.el.select('input.form-control',true).first();
14906     },
14907     
14908     onTickableFooterButtonClick : function(e, btn, el)
14909     {
14910         e.preventDefault();
14911         
14912         this.lastItem = Roo.apply([], this.item);
14913         
14914         if(btn && btn.name == 'cancel'){
14915             this.tickItems = Roo.apply([], this.item);
14916             this.collapse();
14917             return;
14918         }
14919         
14920         this.clearItem();
14921         
14922         var _this = this;
14923         
14924         Roo.each(this.tickItems, function(o){
14925             _this.addItem(o);
14926         });
14927         
14928         this.collapse();
14929         
14930     },
14931     
14932     validate : function()
14933     {
14934         if(this.getVisibilityEl().hasClass('hidden')){
14935             return true;
14936         }
14937         
14938         var v = this.getRawValue();
14939         
14940         if(this.multiple){
14941             v = this.getValue();
14942         }
14943         
14944         if(this.disabled || this.allowBlank || v.length){
14945             this.markValid();
14946             return true;
14947         }
14948         
14949         this.markInvalid();
14950         return false;
14951     },
14952     
14953     tickableInputEl : function()
14954     {
14955         if(!this.tickable || !this.editable){
14956             return this.inputEl();
14957         }
14958         
14959         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14960     },
14961     
14962     
14963     getAutoCreateTouchView : function()
14964     {
14965         var id = Roo.id();
14966         
14967         var cfg = {
14968             cls: 'form-group' //input-group
14969         };
14970         
14971         var input =  {
14972             tag: 'input',
14973             id : id,
14974             type : this.inputType,
14975             cls : 'form-control x-combo-noedit',
14976             autocomplete: 'new-password',
14977             placeholder : this.placeholder || '',
14978             readonly : true
14979         };
14980         
14981         if (this.name) {
14982             input.name = this.name;
14983         }
14984         
14985         if (this.size) {
14986             input.cls += ' input-' + this.size;
14987         }
14988         
14989         if (this.disabled) {
14990             input.disabled = true;
14991         }
14992         
14993         var inputblock = {
14994             cls : '',
14995             cn : [
14996                 input
14997             ]
14998         };
14999         
15000         if(this.before){
15001             inputblock.cls += ' input-group';
15002             
15003             inputblock.cn.unshift({
15004                 tag :'span',
15005                 cls : 'input-group-addon',
15006                 html : this.before
15007             });
15008         }
15009         
15010         if(this.removable && !this.multiple){
15011             inputblock.cls += ' roo-removable';
15012             
15013             inputblock.cn.push({
15014                 tag: 'button',
15015                 html : 'x',
15016                 cls : 'roo-combo-removable-btn close'
15017             });
15018         }
15019
15020         if(this.hasFeedback && !this.allowBlank){
15021             
15022             inputblock.cls += ' has-feedback';
15023             
15024             inputblock.cn.push({
15025                 tag: 'span',
15026                 cls: 'glyphicon form-control-feedback'
15027             });
15028             
15029         }
15030         
15031         if (this.after) {
15032             
15033             inputblock.cls += (this.before) ? '' : ' input-group';
15034             
15035             inputblock.cn.push({
15036                 tag :'span',
15037                 cls : 'input-group-addon',
15038                 html : this.after
15039             });
15040         }
15041
15042         var box = {
15043             tag: 'div',
15044             cn: [
15045                 {
15046                     tag: 'input',
15047                     type : 'hidden',
15048                     cls: 'form-hidden-field'
15049                 },
15050                 inputblock
15051             ]
15052             
15053         };
15054         
15055         if(this.multiple){
15056             box = {
15057                 tag: 'div',
15058                 cn: [
15059                     {
15060                         tag: 'input',
15061                         type : 'hidden',
15062                         cls: 'form-hidden-field'
15063                     },
15064                     {
15065                         tag: 'ul',
15066                         cls: 'roo-select2-choices',
15067                         cn:[
15068                             {
15069                                 tag: 'li',
15070                                 cls: 'roo-select2-search-field',
15071                                 cn: [
15072
15073                                     inputblock
15074                                 ]
15075                             }
15076                         ]
15077                     }
15078                 ]
15079             }
15080         };
15081         
15082         var combobox = {
15083             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15084             cn: [
15085                 box
15086             ]
15087         };
15088         
15089         if(!this.multiple && this.showToggleBtn){
15090             
15091             var caret = {
15092                         tag: 'span',
15093                         cls: 'caret'
15094             };
15095             
15096             if (this.caret != false) {
15097                 caret = {
15098                      tag: 'i',
15099                      cls: 'fa fa-' + this.caret
15100                 };
15101                 
15102             }
15103             
15104             combobox.cn.push({
15105                 tag :'span',
15106                 cls : 'input-group-addon btn dropdown-toggle',
15107                 cn : [
15108                     caret,
15109                     {
15110                         tag: 'span',
15111                         cls: 'combobox-clear',
15112                         cn  : [
15113                             {
15114                                 tag : 'i',
15115                                 cls: 'icon-remove'
15116                             }
15117                         ]
15118                     }
15119                 ]
15120
15121             })
15122         }
15123         
15124         if(this.multiple){
15125             combobox.cls += ' roo-select2-container-multi';
15126         }
15127         
15128         var align = this.labelAlign || this.parentLabelAlign();
15129         
15130         if (align ==='left' && this.fieldLabel.length) {
15131
15132             cfg.cn = [
15133                 {
15134                    tag : 'i',
15135                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15136                    tooltip : 'This field is required'
15137                 },
15138                 {
15139                     tag: 'label',
15140                     cls : 'control-label',
15141                     html : this.fieldLabel
15142
15143                 },
15144                 {
15145                     cls : '', 
15146                     cn: [
15147                         combobox
15148                     ]
15149                 }
15150             ];
15151             
15152             var labelCfg = cfg.cn[1];
15153             var contentCfg = cfg.cn[2];
15154             
15155
15156             if(this.indicatorpos == 'right'){
15157                 cfg.cn = [
15158                     {
15159                         tag: 'label',
15160                         'for' :  id,
15161                         cls : 'control-label',
15162                         cn : [
15163                             {
15164                                 tag : 'span',
15165                                 html : this.fieldLabel
15166                             },
15167                             {
15168                                 tag : 'i',
15169                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15170                                 tooltip : 'This field is required'
15171                             }
15172                         ]
15173                     },
15174                     {
15175                         cls : "",
15176                         cn: [
15177                             combobox
15178                         ]
15179                     }
15180
15181                 ];
15182                 
15183                 labelCfg = cfg.cn[0];
15184                 contentCfg = cfg.cn[1];
15185             }
15186             
15187            
15188             
15189             if(this.labelWidth > 12){
15190                 labelCfg.style = "width: " + this.labelWidth + 'px';
15191             }
15192             
15193             if(this.labelWidth < 13 && this.labelmd == 0){
15194                 this.labelmd = this.labelWidth;
15195             }
15196             
15197             if(this.labellg > 0){
15198                 labelCfg.cls += ' col-lg-' + this.labellg;
15199                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15200             }
15201             
15202             if(this.labelmd > 0){
15203                 labelCfg.cls += ' col-md-' + this.labelmd;
15204                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15205             }
15206             
15207             if(this.labelsm > 0){
15208                 labelCfg.cls += ' col-sm-' + this.labelsm;
15209                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15210             }
15211             
15212             if(this.labelxs > 0){
15213                 labelCfg.cls += ' col-xs-' + this.labelxs;
15214                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15215             }
15216                 
15217                 
15218         } else if ( this.fieldLabel.length) {
15219             cfg.cn = [
15220                 {
15221                    tag : 'i',
15222                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15223                    tooltip : 'This field is required'
15224                 },
15225                 {
15226                     tag: 'label',
15227                     cls : 'control-label',
15228                     html : this.fieldLabel
15229
15230                 },
15231                 {
15232                     cls : '', 
15233                     cn: [
15234                         combobox
15235                     ]
15236                 }
15237             ];
15238             
15239             if(this.indicatorpos == 'right'){
15240                 cfg.cn = [
15241                     {
15242                         tag: 'label',
15243                         cls : 'control-label',
15244                         html : this.fieldLabel,
15245                         cn : [
15246                             {
15247                                tag : 'i',
15248                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15249                                tooltip : 'This field is required'
15250                             }
15251                         ]
15252                     },
15253                     {
15254                         cls : '', 
15255                         cn: [
15256                             combobox
15257                         ]
15258                     }
15259                 ];
15260             }
15261         } else {
15262             cfg.cn = combobox;    
15263         }
15264         
15265         
15266         var settings = this;
15267         
15268         ['xs','sm','md','lg'].map(function(size){
15269             if (settings[size]) {
15270                 cfg.cls += ' col-' + size + '-' + settings[size];
15271             }
15272         });
15273         
15274         return cfg;
15275     },
15276     
15277     initTouchView : function()
15278     {
15279         this.renderTouchView();
15280         
15281         this.touchViewEl.on('scroll', function(){
15282             this.el.dom.scrollTop = 0;
15283         }, this);
15284         
15285         this.originalValue = this.getValue();
15286         
15287         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15288         
15289         this.inputEl().on("click", this.showTouchView, this);
15290         if (this.triggerEl) {
15291             this.triggerEl.on("click", this.showTouchView, this);
15292         }
15293         
15294         
15295         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15296         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15297         
15298         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15299         
15300         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15301         this.store.on('load', this.onTouchViewLoad, this);
15302         this.store.on('loadexception', this.onTouchViewLoadException, this);
15303         
15304         if(this.hiddenName){
15305             
15306             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15307             
15308             this.hiddenField.dom.value =
15309                 this.hiddenValue !== undefined ? this.hiddenValue :
15310                 this.value !== undefined ? this.value : '';
15311         
15312             this.el.dom.removeAttribute('name');
15313             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15314         }
15315         
15316         if(this.multiple){
15317             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15318             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15319         }
15320         
15321         if(this.removable && !this.multiple){
15322             var close = this.closeTriggerEl();
15323             if(close){
15324                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15325                 close.on('click', this.removeBtnClick, this, close);
15326             }
15327         }
15328         /*
15329          * fix the bug in Safari iOS8
15330          */
15331         this.inputEl().on("focus", function(e){
15332             document.activeElement.blur();
15333         }, this);
15334         
15335         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15336         
15337         return;
15338         
15339         
15340     },
15341     
15342     renderTouchView : function()
15343     {
15344         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15345         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15346         
15347         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15348         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15349         
15350         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15351         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15352         this.touchViewBodyEl.setStyle('overflow', 'auto');
15353         
15354         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15355         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15356         
15357         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15358         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15359         
15360     },
15361     
15362     showTouchView : function()
15363     {
15364         if(this.disabled){
15365             return;
15366         }
15367         
15368         this.touchViewHeaderEl.hide();
15369
15370         if(this.modalTitle.length){
15371             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15372             this.touchViewHeaderEl.show();
15373         }
15374
15375         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15376         this.touchViewEl.show();
15377
15378         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15379         
15380         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15381         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15382
15383         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15384
15385         if(this.modalTitle.length){
15386             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15387         }
15388         
15389         this.touchViewBodyEl.setHeight(bodyHeight);
15390
15391         if(this.animate){
15392             var _this = this;
15393             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15394         }else{
15395             this.touchViewEl.addClass('in');
15396         }
15397         
15398         if(this._touchViewMask){
15399             Roo.get(document.body).addClass("x-body-masked");
15400             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15401             this._touchViewMask.setStyle('z-index', 10000);
15402             this._touchViewMask.addClass('show');
15403         }
15404         
15405         this.doTouchViewQuery();
15406         
15407     },
15408     
15409     hideTouchView : function()
15410     {
15411         this.touchViewEl.removeClass('in');
15412
15413         if(this.animate){
15414             var _this = this;
15415             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15416         }else{
15417             this.touchViewEl.setStyle('display', 'none');
15418         }
15419         
15420         if(this._touchViewMask){
15421             this._touchViewMask.removeClass('show');
15422             Roo.get(document.body).removeClass("x-body-masked");
15423         }
15424     },
15425     
15426     setTouchViewValue : function()
15427     {
15428         if(this.multiple){
15429             this.clearItem();
15430         
15431             var _this = this;
15432
15433             Roo.each(this.tickItems, function(o){
15434                 this.addItem(o);
15435             }, this);
15436         }
15437         
15438         this.hideTouchView();
15439     },
15440     
15441     doTouchViewQuery : function()
15442     {
15443         var qe = {
15444             query: '',
15445             forceAll: true,
15446             combo: this,
15447             cancel:false
15448         };
15449         
15450         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15451             return false;
15452         }
15453         
15454         if(!this.alwaysQuery || this.mode == 'local'){
15455             this.onTouchViewLoad();
15456             return;
15457         }
15458         
15459         this.store.load();
15460     },
15461     
15462     onTouchViewBeforeLoad : function(combo,opts)
15463     {
15464         return;
15465     },
15466
15467     // private
15468     onTouchViewLoad : function()
15469     {
15470         if(this.store.getCount() < 1){
15471             this.onTouchViewEmptyResults();
15472             return;
15473         }
15474         
15475         this.clearTouchView();
15476         
15477         var rawValue = this.getRawValue();
15478         
15479         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15480         
15481         this.tickItems = [];
15482         
15483         this.store.data.each(function(d, rowIndex){
15484             var row = this.touchViewListGroup.createChild(template);
15485             
15486             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15487                 row.addClass(d.data.cls);
15488             }
15489             
15490             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15491                 var cfg = {
15492                     data : d.data,
15493                     html : d.data[this.displayField]
15494                 };
15495                 
15496                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15497                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15498                 }
15499             }
15500             row.removeClass('selected');
15501             if(!this.multiple && this.valueField &&
15502                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15503             {
15504                 // radio buttons..
15505                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15506                 row.addClass('selected');
15507             }
15508             
15509             if(this.multiple && this.valueField &&
15510                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15511             {
15512                 
15513                 // checkboxes...
15514                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15515                 this.tickItems.push(d.data);
15516             }
15517             
15518             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15519             
15520         }, this);
15521         
15522         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15523         
15524         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15525
15526         if(this.modalTitle.length){
15527             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15528         }
15529
15530         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15531         
15532         if(this.mobile_restrict_height && listHeight < bodyHeight){
15533             this.touchViewBodyEl.setHeight(listHeight);
15534         }
15535         
15536         var _this = this;
15537         
15538         if(firstChecked && listHeight > bodyHeight){
15539             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15540         }
15541         
15542     },
15543     
15544     onTouchViewLoadException : function()
15545     {
15546         this.hideTouchView();
15547     },
15548     
15549     onTouchViewEmptyResults : function()
15550     {
15551         this.clearTouchView();
15552         
15553         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15554         
15555         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15556         
15557     },
15558     
15559     clearTouchView : function()
15560     {
15561         this.touchViewListGroup.dom.innerHTML = '';
15562     },
15563     
15564     onTouchViewClick : function(e, el, o)
15565     {
15566         e.preventDefault();
15567         
15568         var row = o.row;
15569         var rowIndex = o.rowIndex;
15570         
15571         var r = this.store.getAt(rowIndex);
15572         
15573         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15574             
15575             if(!this.multiple){
15576                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15577                     c.dom.removeAttribute('checked');
15578                 }, this);
15579
15580                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15581
15582                 this.setFromData(r.data);
15583
15584                 var close = this.closeTriggerEl();
15585
15586                 if(close){
15587                     close.show();
15588                 }
15589
15590                 this.hideTouchView();
15591
15592                 this.fireEvent('select', this, r, rowIndex);
15593
15594                 return;
15595             }
15596
15597             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15598                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15599                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15600                 return;
15601             }
15602
15603             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15604             this.addItem(r.data);
15605             this.tickItems.push(r.data);
15606         }
15607     },
15608     
15609     getAutoCreateNativeIOS : function()
15610     {
15611         var cfg = {
15612             cls: 'form-group' //input-group,
15613         };
15614         
15615         var combobox =  {
15616             tag: 'select',
15617             cls : 'roo-ios-select'
15618         };
15619         
15620         if (this.name) {
15621             combobox.name = this.name;
15622         }
15623         
15624         if (this.disabled) {
15625             combobox.disabled = true;
15626         }
15627         
15628         var settings = this;
15629         
15630         ['xs','sm','md','lg'].map(function(size){
15631             if (settings[size]) {
15632                 cfg.cls += ' col-' + size + '-' + settings[size];
15633             }
15634         });
15635         
15636         cfg.cn = combobox;
15637         
15638         return cfg;
15639         
15640     },
15641     
15642     initIOSView : function()
15643     {
15644         this.store.on('load', this.onIOSViewLoad, this);
15645         
15646         return;
15647     },
15648     
15649     onIOSViewLoad : function()
15650     {
15651         if(this.store.getCount() < 1){
15652             return;
15653         }
15654         
15655         this.clearIOSView();
15656         
15657         if(this.allowBlank) {
15658             
15659             var default_text = '-- SELECT --';
15660             
15661             if(this.placeholder.length){
15662                 default_text = this.placeholder;
15663             }
15664             
15665             if(this.emptyTitle.length){
15666                 default_text += ' - ' + this.emptyTitle + ' -';
15667             }
15668             
15669             var opt = this.inputEl().createChild({
15670                 tag: 'option',
15671                 value : 0,
15672                 html : default_text
15673             });
15674             
15675             var o = {};
15676             o[this.valueField] = 0;
15677             o[this.displayField] = default_text;
15678             
15679             this.ios_options.push({
15680                 data : o,
15681                 el : opt
15682             });
15683             
15684         }
15685         
15686         this.store.data.each(function(d, rowIndex){
15687             
15688             var html = '';
15689             
15690             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15691                 html = d.data[this.displayField];
15692             }
15693             
15694             var value = '';
15695             
15696             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15697                 value = d.data[this.valueField];
15698             }
15699             
15700             var option = {
15701                 tag: 'option',
15702                 value : value,
15703                 html : html
15704             };
15705             
15706             if(this.value == d.data[this.valueField]){
15707                 option['selected'] = true;
15708             }
15709             
15710             var opt = this.inputEl().createChild(option);
15711             
15712             this.ios_options.push({
15713                 data : d.data,
15714                 el : opt
15715             });
15716             
15717         }, this);
15718         
15719         this.inputEl().on('change', function(){
15720            this.fireEvent('select', this);
15721         }, this);
15722         
15723     },
15724     
15725     clearIOSView: function()
15726     {
15727         this.inputEl().dom.innerHTML = '';
15728         
15729         this.ios_options = [];
15730     },
15731     
15732     setIOSValue: function(v)
15733     {
15734         this.value = v;
15735         
15736         if(!this.ios_options){
15737             return;
15738         }
15739         
15740         Roo.each(this.ios_options, function(opts){
15741            
15742            opts.el.dom.removeAttribute('selected');
15743            
15744            if(opts.data[this.valueField] != v){
15745                return;
15746            }
15747            
15748            opts.el.dom.setAttribute('selected', true);
15749            
15750         }, this);
15751     }
15752
15753     /** 
15754     * @cfg {Boolean} grow 
15755     * @hide 
15756     */
15757     /** 
15758     * @cfg {Number} growMin 
15759     * @hide 
15760     */
15761     /** 
15762     * @cfg {Number} growMax 
15763     * @hide 
15764     */
15765     /**
15766      * @hide
15767      * @method autoSize
15768      */
15769 });
15770
15771 Roo.apply(Roo.bootstrap.ComboBox,  {
15772     
15773     header : {
15774         tag: 'div',
15775         cls: 'modal-header',
15776         cn: [
15777             {
15778                 tag: 'h4',
15779                 cls: 'modal-title'
15780             }
15781         ]
15782     },
15783     
15784     body : {
15785         tag: 'div',
15786         cls: 'modal-body',
15787         cn: [
15788             {
15789                 tag: 'ul',
15790                 cls: 'list-group'
15791             }
15792         ]
15793     },
15794     
15795     listItemRadio : {
15796         tag: 'li',
15797         cls: 'list-group-item',
15798         cn: [
15799             {
15800                 tag: 'span',
15801                 cls: 'roo-combobox-list-group-item-value'
15802             },
15803             {
15804                 tag: 'div',
15805                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15806                 cn: [
15807                     {
15808                         tag: 'input',
15809                         type: 'radio'
15810                     },
15811                     {
15812                         tag: 'label'
15813                     }
15814                 ]
15815             }
15816         ]
15817     },
15818     
15819     listItemCheckbox : {
15820         tag: 'li',
15821         cls: 'list-group-item',
15822         cn: [
15823             {
15824                 tag: 'span',
15825                 cls: 'roo-combobox-list-group-item-value'
15826             },
15827             {
15828                 tag: 'div',
15829                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15830                 cn: [
15831                     {
15832                         tag: 'input',
15833                         type: 'checkbox'
15834                     },
15835                     {
15836                         tag: 'label'
15837                     }
15838                 ]
15839             }
15840         ]
15841     },
15842     
15843     emptyResult : {
15844         tag: 'div',
15845         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15846     },
15847     
15848     footer : {
15849         tag: 'div',
15850         cls: 'modal-footer',
15851         cn: [
15852             {
15853                 tag: 'div',
15854                 cls: 'row',
15855                 cn: [
15856                     {
15857                         tag: 'div',
15858                         cls: 'col-xs-6 text-left',
15859                         cn: {
15860                             tag: 'button',
15861                             cls: 'btn btn-danger roo-touch-view-cancel',
15862                             html: 'Cancel'
15863                         }
15864                     },
15865                     {
15866                         tag: 'div',
15867                         cls: 'col-xs-6 text-right',
15868                         cn: {
15869                             tag: 'button',
15870                             cls: 'btn btn-success roo-touch-view-ok',
15871                             html: 'OK'
15872                         }
15873                     }
15874                 ]
15875             }
15876         ]
15877         
15878     }
15879 });
15880
15881 Roo.apply(Roo.bootstrap.ComboBox,  {
15882     
15883     touchViewTemplate : {
15884         tag: 'div',
15885         cls: 'modal fade roo-combobox-touch-view',
15886         cn: [
15887             {
15888                 tag: 'div',
15889                 cls: 'modal-dialog',
15890                 style : 'position:fixed', // we have to fix position....
15891                 cn: [
15892                     {
15893                         tag: 'div',
15894                         cls: 'modal-content',
15895                         cn: [
15896                             Roo.bootstrap.ComboBox.header,
15897                             Roo.bootstrap.ComboBox.body,
15898                             Roo.bootstrap.ComboBox.footer
15899                         ]
15900                     }
15901                 ]
15902             }
15903         ]
15904     }
15905 });/*
15906  * Based on:
15907  * Ext JS Library 1.1.1
15908  * Copyright(c) 2006-2007, Ext JS, LLC.
15909  *
15910  * Originally Released Under LGPL - original licence link has changed is not relivant.
15911  *
15912  * Fork - LGPL
15913  * <script type="text/javascript">
15914  */
15915
15916 /**
15917  * @class Roo.View
15918  * @extends Roo.util.Observable
15919  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15920  * This class also supports single and multi selection modes. <br>
15921  * Create a data model bound view:
15922  <pre><code>
15923  var store = new Roo.data.Store(...);
15924
15925  var view = new Roo.View({
15926     el : "my-element",
15927     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15928  
15929     singleSelect: true,
15930     selectedClass: "ydataview-selected",
15931     store: store
15932  });
15933
15934  // listen for node click?
15935  view.on("click", function(vw, index, node, e){
15936  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15937  });
15938
15939  // load XML data
15940  dataModel.load("foobar.xml");
15941  </code></pre>
15942  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15943  * <br><br>
15944  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15945  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15946  * 
15947  * Note: old style constructor is still suported (container, template, config)
15948  * 
15949  * @constructor
15950  * Create a new View
15951  * @param {Object} config The config object
15952  * 
15953  */
15954 Roo.View = function(config, depreciated_tpl, depreciated_config){
15955     
15956     this.parent = false;
15957     
15958     if (typeof(depreciated_tpl) == 'undefined') {
15959         // new way.. - universal constructor.
15960         Roo.apply(this, config);
15961         this.el  = Roo.get(this.el);
15962     } else {
15963         // old format..
15964         this.el  = Roo.get(config);
15965         this.tpl = depreciated_tpl;
15966         Roo.apply(this, depreciated_config);
15967     }
15968     this.wrapEl  = this.el.wrap().wrap();
15969     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15970     
15971     
15972     if(typeof(this.tpl) == "string"){
15973         this.tpl = new Roo.Template(this.tpl);
15974     } else {
15975         // support xtype ctors..
15976         this.tpl = new Roo.factory(this.tpl, Roo);
15977     }
15978     
15979     
15980     this.tpl.compile();
15981     
15982     /** @private */
15983     this.addEvents({
15984         /**
15985          * @event beforeclick
15986          * Fires before a click is processed. Returns false to cancel the default action.
15987          * @param {Roo.View} this
15988          * @param {Number} index The index of the target node
15989          * @param {HTMLElement} node The target node
15990          * @param {Roo.EventObject} e The raw event object
15991          */
15992             "beforeclick" : true,
15993         /**
15994          * @event click
15995          * Fires when a template node is clicked.
15996          * @param {Roo.View} this
15997          * @param {Number} index The index of the target node
15998          * @param {HTMLElement} node The target node
15999          * @param {Roo.EventObject} e The raw event object
16000          */
16001             "click" : true,
16002         /**
16003          * @event dblclick
16004          * Fires when a template node is double clicked.
16005          * @param {Roo.View} this
16006          * @param {Number} index The index of the target node
16007          * @param {HTMLElement} node The target node
16008          * @param {Roo.EventObject} e The raw event object
16009          */
16010             "dblclick" : true,
16011         /**
16012          * @event contextmenu
16013          * Fires when a template node is right clicked.
16014          * @param {Roo.View} this
16015          * @param {Number} index The index of the target node
16016          * @param {HTMLElement} node The target node
16017          * @param {Roo.EventObject} e The raw event object
16018          */
16019             "contextmenu" : true,
16020         /**
16021          * @event selectionchange
16022          * Fires when the selected nodes change.
16023          * @param {Roo.View} this
16024          * @param {Array} selections Array of the selected nodes
16025          */
16026             "selectionchange" : true,
16027     
16028         /**
16029          * @event beforeselect
16030          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16031          * @param {Roo.View} this
16032          * @param {HTMLElement} node The node to be selected
16033          * @param {Array} selections Array of currently selected nodes
16034          */
16035             "beforeselect" : true,
16036         /**
16037          * @event preparedata
16038          * Fires on every row to render, to allow you to change the data.
16039          * @param {Roo.View} this
16040          * @param {Object} data to be rendered (change this)
16041          */
16042           "preparedata" : true
16043           
16044           
16045         });
16046
16047
16048
16049     this.el.on({
16050         "click": this.onClick,
16051         "dblclick": this.onDblClick,
16052         "contextmenu": this.onContextMenu,
16053         scope:this
16054     });
16055
16056     this.selections = [];
16057     this.nodes = [];
16058     this.cmp = new Roo.CompositeElementLite([]);
16059     if(this.store){
16060         this.store = Roo.factory(this.store, Roo.data);
16061         this.setStore(this.store, true);
16062     }
16063     
16064     if ( this.footer && this.footer.xtype) {
16065            
16066          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16067         
16068         this.footer.dataSource = this.store;
16069         this.footer.container = fctr;
16070         this.footer = Roo.factory(this.footer, Roo);
16071         fctr.insertFirst(this.el);
16072         
16073         // this is a bit insane - as the paging toolbar seems to detach the el..
16074 //        dom.parentNode.parentNode.parentNode
16075          // they get detached?
16076     }
16077     
16078     
16079     Roo.View.superclass.constructor.call(this);
16080     
16081     
16082 };
16083
16084 Roo.extend(Roo.View, Roo.util.Observable, {
16085     
16086      /**
16087      * @cfg {Roo.data.Store} store Data store to load data from.
16088      */
16089     store : false,
16090     
16091     /**
16092      * @cfg {String|Roo.Element} el The container element.
16093      */
16094     el : '',
16095     
16096     /**
16097      * @cfg {String|Roo.Template} tpl The template used by this View 
16098      */
16099     tpl : false,
16100     /**
16101      * @cfg {String} dataName the named area of the template to use as the data area
16102      *                          Works with domtemplates roo-name="name"
16103      */
16104     dataName: false,
16105     /**
16106      * @cfg {String} selectedClass The css class to add to selected nodes
16107      */
16108     selectedClass : "x-view-selected",
16109      /**
16110      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16111      */
16112     emptyText : "",
16113     
16114     /**
16115      * @cfg {String} text to display on mask (default Loading)
16116      */
16117     mask : false,
16118     /**
16119      * @cfg {Boolean} multiSelect Allow multiple selection
16120      */
16121     multiSelect : false,
16122     /**
16123      * @cfg {Boolean} singleSelect Allow single selection
16124      */
16125     singleSelect:  false,
16126     
16127     /**
16128      * @cfg {Boolean} toggleSelect - selecting 
16129      */
16130     toggleSelect : false,
16131     
16132     /**
16133      * @cfg {Boolean} tickable - selecting 
16134      */
16135     tickable : false,
16136     
16137     /**
16138      * Returns the element this view is bound to.
16139      * @return {Roo.Element}
16140      */
16141     getEl : function(){
16142         return this.wrapEl;
16143     },
16144     
16145     
16146
16147     /**
16148      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16149      */
16150     refresh : function(){
16151         //Roo.log('refresh');
16152         var t = this.tpl;
16153         
16154         // if we are using something like 'domtemplate', then
16155         // the what gets used is:
16156         // t.applySubtemplate(NAME, data, wrapping data..)
16157         // the outer template then get' applied with
16158         //     the store 'extra data'
16159         // and the body get's added to the
16160         //      roo-name="data" node?
16161         //      <span class='roo-tpl-{name}'></span> ?????
16162         
16163         
16164         
16165         this.clearSelections();
16166         this.el.update("");
16167         var html = [];
16168         var records = this.store.getRange();
16169         if(records.length < 1) {
16170             
16171             // is this valid??  = should it render a template??
16172             
16173             this.el.update(this.emptyText);
16174             return;
16175         }
16176         var el = this.el;
16177         if (this.dataName) {
16178             this.el.update(t.apply(this.store.meta)); //????
16179             el = this.el.child('.roo-tpl-' + this.dataName);
16180         }
16181         
16182         for(var i = 0, len = records.length; i < len; i++){
16183             var data = this.prepareData(records[i].data, i, records[i]);
16184             this.fireEvent("preparedata", this, data, i, records[i]);
16185             
16186             var d = Roo.apply({}, data);
16187             
16188             if(this.tickable){
16189                 Roo.apply(d, {'roo-id' : Roo.id()});
16190                 
16191                 var _this = this;
16192             
16193                 Roo.each(this.parent.item, function(item){
16194                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16195                         return;
16196                     }
16197                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16198                 });
16199             }
16200             
16201             html[html.length] = Roo.util.Format.trim(
16202                 this.dataName ?
16203                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16204                     t.apply(d)
16205             );
16206         }
16207         
16208         
16209         
16210         el.update(html.join(""));
16211         this.nodes = el.dom.childNodes;
16212         this.updateIndexes(0);
16213     },
16214     
16215
16216     /**
16217      * Function to override to reformat the data that is sent to
16218      * the template for each node.
16219      * DEPRICATED - use the preparedata event handler.
16220      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16221      * a JSON object for an UpdateManager bound view).
16222      */
16223     prepareData : function(data, index, record)
16224     {
16225         this.fireEvent("preparedata", this, data, index, record);
16226         return data;
16227     },
16228
16229     onUpdate : function(ds, record){
16230         // Roo.log('on update');   
16231         this.clearSelections();
16232         var index = this.store.indexOf(record);
16233         var n = this.nodes[index];
16234         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16235         n.parentNode.removeChild(n);
16236         this.updateIndexes(index, index);
16237     },
16238
16239     
16240     
16241 // --------- FIXME     
16242     onAdd : function(ds, records, index)
16243     {
16244         //Roo.log(['on Add', ds, records, index] );        
16245         this.clearSelections();
16246         if(this.nodes.length == 0){
16247             this.refresh();
16248             return;
16249         }
16250         var n = this.nodes[index];
16251         for(var i = 0, len = records.length; i < len; i++){
16252             var d = this.prepareData(records[i].data, i, records[i]);
16253             if(n){
16254                 this.tpl.insertBefore(n, d);
16255             }else{
16256                 
16257                 this.tpl.append(this.el, d);
16258             }
16259         }
16260         this.updateIndexes(index);
16261     },
16262
16263     onRemove : function(ds, record, index){
16264        // Roo.log('onRemove');
16265         this.clearSelections();
16266         var el = this.dataName  ?
16267             this.el.child('.roo-tpl-' + this.dataName) :
16268             this.el; 
16269         
16270         el.dom.removeChild(this.nodes[index]);
16271         this.updateIndexes(index);
16272     },
16273
16274     /**
16275      * Refresh an individual node.
16276      * @param {Number} index
16277      */
16278     refreshNode : function(index){
16279         this.onUpdate(this.store, this.store.getAt(index));
16280     },
16281
16282     updateIndexes : function(startIndex, endIndex){
16283         var ns = this.nodes;
16284         startIndex = startIndex || 0;
16285         endIndex = endIndex || ns.length - 1;
16286         for(var i = startIndex; i <= endIndex; i++){
16287             ns[i].nodeIndex = i;
16288         }
16289     },
16290
16291     /**
16292      * Changes the data store this view uses and refresh the view.
16293      * @param {Store} store
16294      */
16295     setStore : function(store, initial){
16296         if(!initial && this.store){
16297             this.store.un("datachanged", this.refresh);
16298             this.store.un("add", this.onAdd);
16299             this.store.un("remove", this.onRemove);
16300             this.store.un("update", this.onUpdate);
16301             this.store.un("clear", this.refresh);
16302             this.store.un("beforeload", this.onBeforeLoad);
16303             this.store.un("load", this.onLoad);
16304             this.store.un("loadexception", this.onLoad);
16305         }
16306         if(store){
16307           
16308             store.on("datachanged", this.refresh, this);
16309             store.on("add", this.onAdd, this);
16310             store.on("remove", this.onRemove, this);
16311             store.on("update", this.onUpdate, this);
16312             store.on("clear", this.refresh, this);
16313             store.on("beforeload", this.onBeforeLoad, this);
16314             store.on("load", this.onLoad, this);
16315             store.on("loadexception", this.onLoad, this);
16316         }
16317         
16318         if(store){
16319             this.refresh();
16320         }
16321     },
16322     /**
16323      * onbeforeLoad - masks the loading area.
16324      *
16325      */
16326     onBeforeLoad : function(store,opts)
16327     {
16328          //Roo.log('onBeforeLoad');   
16329         if (!opts.add) {
16330             this.el.update("");
16331         }
16332         this.el.mask(this.mask ? this.mask : "Loading" ); 
16333     },
16334     onLoad : function ()
16335     {
16336         this.el.unmask();
16337     },
16338     
16339
16340     /**
16341      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16342      * @param {HTMLElement} node
16343      * @return {HTMLElement} The template node
16344      */
16345     findItemFromChild : function(node){
16346         var el = this.dataName  ?
16347             this.el.child('.roo-tpl-' + this.dataName,true) :
16348             this.el.dom; 
16349         
16350         if(!node || node.parentNode == el){
16351                     return node;
16352             }
16353             var p = node.parentNode;
16354             while(p && p != el){
16355             if(p.parentNode == el){
16356                 return p;
16357             }
16358             p = p.parentNode;
16359         }
16360             return null;
16361     },
16362
16363     /** @ignore */
16364     onClick : function(e){
16365         var item = this.findItemFromChild(e.getTarget());
16366         if(item){
16367             var index = this.indexOf(item);
16368             if(this.onItemClick(item, index, e) !== false){
16369                 this.fireEvent("click", this, index, item, e);
16370             }
16371         }else{
16372             this.clearSelections();
16373         }
16374     },
16375
16376     /** @ignore */
16377     onContextMenu : function(e){
16378         var item = this.findItemFromChild(e.getTarget());
16379         if(item){
16380             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16381         }
16382     },
16383
16384     /** @ignore */
16385     onDblClick : function(e){
16386         var item = this.findItemFromChild(e.getTarget());
16387         if(item){
16388             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16389         }
16390     },
16391
16392     onItemClick : function(item, index, e)
16393     {
16394         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16395             return false;
16396         }
16397         if (this.toggleSelect) {
16398             var m = this.isSelected(item) ? 'unselect' : 'select';
16399             //Roo.log(m);
16400             var _t = this;
16401             _t[m](item, true, false);
16402             return true;
16403         }
16404         if(this.multiSelect || this.singleSelect){
16405             if(this.multiSelect && e.shiftKey && this.lastSelection){
16406                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16407             }else{
16408                 this.select(item, this.multiSelect && e.ctrlKey);
16409                 this.lastSelection = item;
16410             }
16411             
16412             if(!this.tickable){
16413                 e.preventDefault();
16414             }
16415             
16416         }
16417         return true;
16418     },
16419
16420     /**
16421      * Get the number of selected nodes.
16422      * @return {Number}
16423      */
16424     getSelectionCount : function(){
16425         return this.selections.length;
16426     },
16427
16428     /**
16429      * Get the currently selected nodes.
16430      * @return {Array} An array of HTMLElements
16431      */
16432     getSelectedNodes : function(){
16433         return this.selections;
16434     },
16435
16436     /**
16437      * Get the indexes of the selected nodes.
16438      * @return {Array}
16439      */
16440     getSelectedIndexes : function(){
16441         var indexes = [], s = this.selections;
16442         for(var i = 0, len = s.length; i < len; i++){
16443             indexes.push(s[i].nodeIndex);
16444         }
16445         return indexes;
16446     },
16447
16448     /**
16449      * Clear all selections
16450      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16451      */
16452     clearSelections : function(suppressEvent){
16453         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16454             this.cmp.elements = this.selections;
16455             this.cmp.removeClass(this.selectedClass);
16456             this.selections = [];
16457             if(!suppressEvent){
16458                 this.fireEvent("selectionchange", this, this.selections);
16459             }
16460         }
16461     },
16462
16463     /**
16464      * Returns true if the passed node is selected
16465      * @param {HTMLElement/Number} node The node or node index
16466      * @return {Boolean}
16467      */
16468     isSelected : function(node){
16469         var s = this.selections;
16470         if(s.length < 1){
16471             return false;
16472         }
16473         node = this.getNode(node);
16474         return s.indexOf(node) !== -1;
16475     },
16476
16477     /**
16478      * Selects nodes.
16479      * @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
16480      * @param {Boolean} keepExisting (optional) true to keep existing selections
16481      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16482      */
16483     select : function(nodeInfo, keepExisting, suppressEvent){
16484         if(nodeInfo instanceof Array){
16485             if(!keepExisting){
16486                 this.clearSelections(true);
16487             }
16488             for(var i = 0, len = nodeInfo.length; i < len; i++){
16489                 this.select(nodeInfo[i], true, true);
16490             }
16491             return;
16492         } 
16493         var node = this.getNode(nodeInfo);
16494         if(!node || this.isSelected(node)){
16495             return; // already selected.
16496         }
16497         if(!keepExisting){
16498             this.clearSelections(true);
16499         }
16500         
16501         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16502             Roo.fly(node).addClass(this.selectedClass);
16503             this.selections.push(node);
16504             if(!suppressEvent){
16505                 this.fireEvent("selectionchange", this, this.selections);
16506             }
16507         }
16508         
16509         
16510     },
16511       /**
16512      * Unselects nodes.
16513      * @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
16514      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16515      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16516      */
16517     unselect : function(nodeInfo, keepExisting, suppressEvent)
16518     {
16519         if(nodeInfo instanceof Array){
16520             Roo.each(this.selections, function(s) {
16521                 this.unselect(s, nodeInfo);
16522             }, this);
16523             return;
16524         }
16525         var node = this.getNode(nodeInfo);
16526         if(!node || !this.isSelected(node)){
16527             //Roo.log("not selected");
16528             return; // not selected.
16529         }
16530         // fireevent???
16531         var ns = [];
16532         Roo.each(this.selections, function(s) {
16533             if (s == node ) {
16534                 Roo.fly(node).removeClass(this.selectedClass);
16535
16536                 return;
16537             }
16538             ns.push(s);
16539         },this);
16540         
16541         this.selections= ns;
16542         this.fireEvent("selectionchange", this, this.selections);
16543     },
16544
16545     /**
16546      * Gets a template node.
16547      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16548      * @return {HTMLElement} The node or null if it wasn't found
16549      */
16550     getNode : function(nodeInfo){
16551         if(typeof nodeInfo == "string"){
16552             return document.getElementById(nodeInfo);
16553         }else if(typeof nodeInfo == "number"){
16554             return this.nodes[nodeInfo];
16555         }
16556         return nodeInfo;
16557     },
16558
16559     /**
16560      * Gets a range template nodes.
16561      * @param {Number} startIndex
16562      * @param {Number} endIndex
16563      * @return {Array} An array of nodes
16564      */
16565     getNodes : function(start, end){
16566         var ns = this.nodes;
16567         start = start || 0;
16568         end = typeof end == "undefined" ? ns.length - 1 : end;
16569         var nodes = [];
16570         if(start <= end){
16571             for(var i = start; i <= end; i++){
16572                 nodes.push(ns[i]);
16573             }
16574         } else{
16575             for(var i = start; i >= end; i--){
16576                 nodes.push(ns[i]);
16577             }
16578         }
16579         return nodes;
16580     },
16581
16582     /**
16583      * Finds the index of the passed node
16584      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16585      * @return {Number} The index of the node or -1
16586      */
16587     indexOf : function(node){
16588         node = this.getNode(node);
16589         if(typeof node.nodeIndex == "number"){
16590             return node.nodeIndex;
16591         }
16592         var ns = this.nodes;
16593         for(var i = 0, len = ns.length; i < len; i++){
16594             if(ns[i] == node){
16595                 return i;
16596             }
16597         }
16598         return -1;
16599     }
16600 });
16601 /*
16602  * - LGPL
16603  *
16604  * based on jquery fullcalendar
16605  * 
16606  */
16607
16608 Roo.bootstrap = Roo.bootstrap || {};
16609 /**
16610  * @class Roo.bootstrap.Calendar
16611  * @extends Roo.bootstrap.Component
16612  * Bootstrap Calendar class
16613  * @cfg {Boolean} loadMask (true|false) default false
16614  * @cfg {Object} header generate the user specific header of the calendar, default false
16615
16616  * @constructor
16617  * Create a new Container
16618  * @param {Object} config The config object
16619  */
16620
16621
16622
16623 Roo.bootstrap.Calendar = function(config){
16624     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16625      this.addEvents({
16626         /**
16627              * @event select
16628              * Fires when a date is selected
16629              * @param {DatePicker} this
16630              * @param {Date} date The selected date
16631              */
16632         'select': true,
16633         /**
16634              * @event monthchange
16635              * Fires when the displayed month changes 
16636              * @param {DatePicker} this
16637              * @param {Date} date The selected month
16638              */
16639         'monthchange': true,
16640         /**
16641              * @event evententer
16642              * Fires when mouse over an event
16643              * @param {Calendar} this
16644              * @param {event} Event
16645              */
16646         'evententer': true,
16647         /**
16648              * @event eventleave
16649              * Fires when the mouse leaves an
16650              * @param {Calendar} this
16651              * @param {event}
16652              */
16653         'eventleave': true,
16654         /**
16655              * @event eventclick
16656              * Fires when the mouse click an
16657              * @param {Calendar} this
16658              * @param {event}
16659              */
16660         'eventclick': true
16661         
16662     });
16663
16664 };
16665
16666 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16667     
16668      /**
16669      * @cfg {Number} startDay
16670      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16671      */
16672     startDay : 0,
16673     
16674     loadMask : false,
16675     
16676     header : false,
16677       
16678     getAutoCreate : function(){
16679         
16680         
16681         var fc_button = function(name, corner, style, content ) {
16682             return Roo.apply({},{
16683                 tag : 'span',
16684                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16685                          (corner.length ?
16686                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16687                             ''
16688                         ),
16689                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16690                 unselectable: 'on'
16691             });
16692         };
16693         
16694         var header = {};
16695         
16696         if(!this.header){
16697             header = {
16698                 tag : 'table',
16699                 cls : 'fc-header',
16700                 style : 'width:100%',
16701                 cn : [
16702                     {
16703                         tag: 'tr',
16704                         cn : [
16705                             {
16706                                 tag : 'td',
16707                                 cls : 'fc-header-left',
16708                                 cn : [
16709                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16710                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16711                                     { tag: 'span', cls: 'fc-header-space' },
16712                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16713
16714
16715                                 ]
16716                             },
16717
16718                             {
16719                                 tag : 'td',
16720                                 cls : 'fc-header-center',
16721                                 cn : [
16722                                     {
16723                                         tag: 'span',
16724                                         cls: 'fc-header-title',
16725                                         cn : {
16726                                             tag: 'H2',
16727                                             html : 'month / year'
16728                                         }
16729                                     }
16730
16731                                 ]
16732                             },
16733                             {
16734                                 tag : 'td',
16735                                 cls : 'fc-header-right',
16736                                 cn : [
16737                               /*      fc_button('month', 'left', '', 'month' ),
16738                                     fc_button('week', '', '', 'week' ),
16739                                     fc_button('day', 'right', '', 'day' )
16740                                 */    
16741
16742                                 ]
16743                             }
16744
16745                         ]
16746                     }
16747                 ]
16748             };
16749         }
16750         
16751         header = this.header;
16752         
16753        
16754         var cal_heads = function() {
16755             var ret = [];
16756             // fixme - handle this.
16757             
16758             for (var i =0; i < Date.dayNames.length; i++) {
16759                 var d = Date.dayNames[i];
16760                 ret.push({
16761                     tag: 'th',
16762                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16763                     html : d.substring(0,3)
16764                 });
16765                 
16766             }
16767             ret[0].cls += ' fc-first';
16768             ret[6].cls += ' fc-last';
16769             return ret;
16770         };
16771         var cal_cell = function(n) {
16772             return  {
16773                 tag: 'td',
16774                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16775                 cn : [
16776                     {
16777                         cn : [
16778                             {
16779                                 cls: 'fc-day-number',
16780                                 html: 'D'
16781                             },
16782                             {
16783                                 cls: 'fc-day-content',
16784                              
16785                                 cn : [
16786                                      {
16787                                         style: 'position: relative;' // height: 17px;
16788                                     }
16789                                 ]
16790                             }
16791                             
16792                             
16793                         ]
16794                     }
16795                 ]
16796                 
16797             }
16798         };
16799         var cal_rows = function() {
16800             
16801             var ret = [];
16802             for (var r = 0; r < 6; r++) {
16803                 var row= {
16804                     tag : 'tr',
16805                     cls : 'fc-week',
16806                     cn : []
16807                 };
16808                 
16809                 for (var i =0; i < Date.dayNames.length; i++) {
16810                     var d = Date.dayNames[i];
16811                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16812
16813                 }
16814                 row.cn[0].cls+=' fc-first';
16815                 row.cn[0].cn[0].style = 'min-height:90px';
16816                 row.cn[6].cls+=' fc-last';
16817                 ret.push(row);
16818                 
16819             }
16820             ret[0].cls += ' fc-first';
16821             ret[4].cls += ' fc-prev-last';
16822             ret[5].cls += ' fc-last';
16823             return ret;
16824             
16825         };
16826         
16827         var cal_table = {
16828             tag: 'table',
16829             cls: 'fc-border-separate',
16830             style : 'width:100%',
16831             cellspacing  : 0,
16832             cn : [
16833                 { 
16834                     tag: 'thead',
16835                     cn : [
16836                         { 
16837                             tag: 'tr',
16838                             cls : 'fc-first fc-last',
16839                             cn : cal_heads()
16840                         }
16841                     ]
16842                 },
16843                 { 
16844                     tag: 'tbody',
16845                     cn : cal_rows()
16846                 }
16847                   
16848             ]
16849         };
16850          
16851          var cfg = {
16852             cls : 'fc fc-ltr',
16853             cn : [
16854                 header,
16855                 {
16856                     cls : 'fc-content',
16857                     style : "position: relative;",
16858                     cn : [
16859                         {
16860                             cls : 'fc-view fc-view-month fc-grid',
16861                             style : 'position: relative',
16862                             unselectable : 'on',
16863                             cn : [
16864                                 {
16865                                     cls : 'fc-event-container',
16866                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16867                                 },
16868                                 cal_table
16869                             ]
16870                         }
16871                     ]
16872     
16873                 }
16874            ] 
16875             
16876         };
16877         
16878          
16879         
16880         return cfg;
16881     },
16882     
16883     
16884     initEvents : function()
16885     {
16886         if(!this.store){
16887             throw "can not find store for calendar";
16888         }
16889         
16890         var mark = {
16891             tag: "div",
16892             cls:"x-dlg-mask",
16893             style: "text-align:center",
16894             cn: [
16895                 {
16896                     tag: "div",
16897                     style: "background-color:white;width:50%;margin:250 auto",
16898                     cn: [
16899                         {
16900                             tag: "img",
16901                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16902                         },
16903                         {
16904                             tag: "span",
16905                             html: "Loading"
16906                         }
16907                         
16908                     ]
16909                 }
16910             ]
16911         };
16912         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16913         
16914         var size = this.el.select('.fc-content', true).first().getSize();
16915         this.maskEl.setSize(size.width, size.height);
16916         this.maskEl.enableDisplayMode("block");
16917         if(!this.loadMask){
16918             this.maskEl.hide();
16919         }
16920         
16921         this.store = Roo.factory(this.store, Roo.data);
16922         this.store.on('load', this.onLoad, this);
16923         this.store.on('beforeload', this.onBeforeLoad, this);
16924         
16925         this.resize();
16926         
16927         this.cells = this.el.select('.fc-day',true);
16928         //Roo.log(this.cells);
16929         this.textNodes = this.el.query('.fc-day-number');
16930         this.cells.addClassOnOver('fc-state-hover');
16931         
16932         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16933         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16934         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16935         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16936         
16937         this.on('monthchange', this.onMonthChange, this);
16938         
16939         this.update(new Date().clearTime());
16940     },
16941     
16942     resize : function() {
16943         var sz  = this.el.getSize();
16944         
16945         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16946         this.el.select('.fc-day-content div',true).setHeight(34);
16947     },
16948     
16949     
16950     // private
16951     showPrevMonth : function(e){
16952         this.update(this.activeDate.add("mo", -1));
16953     },
16954     showToday : function(e){
16955         this.update(new Date().clearTime());
16956     },
16957     // private
16958     showNextMonth : function(e){
16959         this.update(this.activeDate.add("mo", 1));
16960     },
16961
16962     // private
16963     showPrevYear : function(){
16964         this.update(this.activeDate.add("y", -1));
16965     },
16966
16967     // private
16968     showNextYear : function(){
16969         this.update(this.activeDate.add("y", 1));
16970     },
16971
16972     
16973    // private
16974     update : function(date)
16975     {
16976         var vd = this.activeDate;
16977         this.activeDate = date;
16978 //        if(vd && this.el){
16979 //            var t = date.getTime();
16980 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16981 //                Roo.log('using add remove');
16982 //                
16983 //                this.fireEvent('monthchange', this, date);
16984 //                
16985 //                this.cells.removeClass("fc-state-highlight");
16986 //                this.cells.each(function(c){
16987 //                   if(c.dateValue == t){
16988 //                       c.addClass("fc-state-highlight");
16989 //                       setTimeout(function(){
16990 //                            try{c.dom.firstChild.focus();}catch(e){}
16991 //                       }, 50);
16992 //                       return false;
16993 //                   }
16994 //                   return true;
16995 //                });
16996 //                return;
16997 //            }
16998 //        }
16999         
17000         var days = date.getDaysInMonth();
17001         
17002         var firstOfMonth = date.getFirstDateOfMonth();
17003         var startingPos = firstOfMonth.getDay()-this.startDay;
17004         
17005         if(startingPos < this.startDay){
17006             startingPos += 7;
17007         }
17008         
17009         var pm = date.add(Date.MONTH, -1);
17010         var prevStart = pm.getDaysInMonth()-startingPos;
17011 //        
17012         this.cells = this.el.select('.fc-day',true);
17013         this.textNodes = this.el.query('.fc-day-number');
17014         this.cells.addClassOnOver('fc-state-hover');
17015         
17016         var cells = this.cells.elements;
17017         var textEls = this.textNodes;
17018         
17019         Roo.each(cells, function(cell){
17020             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17021         });
17022         
17023         days += startingPos;
17024
17025         // convert everything to numbers so it's fast
17026         var day = 86400000;
17027         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17028         //Roo.log(d);
17029         //Roo.log(pm);
17030         //Roo.log(prevStart);
17031         
17032         var today = new Date().clearTime().getTime();
17033         var sel = date.clearTime().getTime();
17034         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17035         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17036         var ddMatch = this.disabledDatesRE;
17037         var ddText = this.disabledDatesText;
17038         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17039         var ddaysText = this.disabledDaysText;
17040         var format = this.format;
17041         
17042         var setCellClass = function(cal, cell){
17043             cell.row = 0;
17044             cell.events = [];
17045             cell.more = [];
17046             //Roo.log('set Cell Class');
17047             cell.title = "";
17048             var t = d.getTime();
17049             
17050             //Roo.log(d);
17051             
17052             cell.dateValue = t;
17053             if(t == today){
17054                 cell.className += " fc-today";
17055                 cell.className += " fc-state-highlight";
17056                 cell.title = cal.todayText;
17057             }
17058             if(t == sel){
17059                 // disable highlight in other month..
17060                 //cell.className += " fc-state-highlight";
17061                 
17062             }
17063             // disabling
17064             if(t < min) {
17065                 cell.className = " fc-state-disabled";
17066                 cell.title = cal.minText;
17067                 return;
17068             }
17069             if(t > max) {
17070                 cell.className = " fc-state-disabled";
17071                 cell.title = cal.maxText;
17072                 return;
17073             }
17074             if(ddays){
17075                 if(ddays.indexOf(d.getDay()) != -1){
17076                     cell.title = ddaysText;
17077                     cell.className = " fc-state-disabled";
17078                 }
17079             }
17080             if(ddMatch && format){
17081                 var fvalue = d.dateFormat(format);
17082                 if(ddMatch.test(fvalue)){
17083                     cell.title = ddText.replace("%0", fvalue);
17084                     cell.className = " fc-state-disabled";
17085                 }
17086             }
17087             
17088             if (!cell.initialClassName) {
17089                 cell.initialClassName = cell.dom.className;
17090             }
17091             
17092             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17093         };
17094
17095         var i = 0;
17096         
17097         for(; i < startingPos; i++) {
17098             textEls[i].innerHTML = (++prevStart);
17099             d.setDate(d.getDate()+1);
17100             
17101             cells[i].className = "fc-past fc-other-month";
17102             setCellClass(this, cells[i]);
17103         }
17104         
17105         var intDay = 0;
17106         
17107         for(; i < days; i++){
17108             intDay = i - startingPos + 1;
17109             textEls[i].innerHTML = (intDay);
17110             d.setDate(d.getDate()+1);
17111             
17112             cells[i].className = ''; // "x-date-active";
17113             setCellClass(this, cells[i]);
17114         }
17115         var extraDays = 0;
17116         
17117         for(; i < 42; i++) {
17118             textEls[i].innerHTML = (++extraDays);
17119             d.setDate(d.getDate()+1);
17120             
17121             cells[i].className = "fc-future fc-other-month";
17122             setCellClass(this, cells[i]);
17123         }
17124         
17125         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17126         
17127         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17128         
17129         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17130         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17131         
17132         if(totalRows != 6){
17133             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17134             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17135         }
17136         
17137         this.fireEvent('monthchange', this, date);
17138         
17139         
17140         /*
17141         if(!this.internalRender){
17142             var main = this.el.dom.firstChild;
17143             var w = main.offsetWidth;
17144             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17145             Roo.fly(main).setWidth(w);
17146             this.internalRender = true;
17147             // opera does not respect the auto grow header center column
17148             // then, after it gets a width opera refuses to recalculate
17149             // without a second pass
17150             if(Roo.isOpera && !this.secondPass){
17151                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17152                 this.secondPass = true;
17153                 this.update.defer(10, this, [date]);
17154             }
17155         }
17156         */
17157         
17158     },
17159     
17160     findCell : function(dt) {
17161         dt = dt.clearTime().getTime();
17162         var ret = false;
17163         this.cells.each(function(c){
17164             //Roo.log("check " +c.dateValue + '?=' + dt);
17165             if(c.dateValue == dt){
17166                 ret = c;
17167                 return false;
17168             }
17169             return true;
17170         });
17171         
17172         return ret;
17173     },
17174     
17175     findCells : function(ev) {
17176         var s = ev.start.clone().clearTime().getTime();
17177        // Roo.log(s);
17178         var e= ev.end.clone().clearTime().getTime();
17179        // Roo.log(e);
17180         var ret = [];
17181         this.cells.each(function(c){
17182              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17183             
17184             if(c.dateValue > e){
17185                 return ;
17186             }
17187             if(c.dateValue < s){
17188                 return ;
17189             }
17190             ret.push(c);
17191         });
17192         
17193         return ret;    
17194     },
17195     
17196 //    findBestRow: function(cells)
17197 //    {
17198 //        var ret = 0;
17199 //        
17200 //        for (var i =0 ; i < cells.length;i++) {
17201 //            ret  = Math.max(cells[i].rows || 0,ret);
17202 //        }
17203 //        return ret;
17204 //        
17205 //    },
17206     
17207     
17208     addItem : function(ev)
17209     {
17210         // look for vertical location slot in
17211         var cells = this.findCells(ev);
17212         
17213 //        ev.row = this.findBestRow(cells);
17214         
17215         // work out the location.
17216         
17217         var crow = false;
17218         var rows = [];
17219         for(var i =0; i < cells.length; i++) {
17220             
17221             cells[i].row = cells[0].row;
17222             
17223             if(i == 0){
17224                 cells[i].row = cells[i].row + 1;
17225             }
17226             
17227             if (!crow) {
17228                 crow = {
17229                     start : cells[i],
17230                     end :  cells[i]
17231                 };
17232                 continue;
17233             }
17234             if (crow.start.getY() == cells[i].getY()) {
17235                 // on same row.
17236                 crow.end = cells[i];
17237                 continue;
17238             }
17239             // different row.
17240             rows.push(crow);
17241             crow = {
17242                 start: cells[i],
17243                 end : cells[i]
17244             };
17245             
17246         }
17247         
17248         rows.push(crow);
17249         ev.els = [];
17250         ev.rows = rows;
17251         ev.cells = cells;
17252         
17253         cells[0].events.push(ev);
17254         
17255         this.calevents.push(ev);
17256     },
17257     
17258     clearEvents: function() {
17259         
17260         if(!this.calevents){
17261             return;
17262         }
17263         
17264         Roo.each(this.cells.elements, function(c){
17265             c.row = 0;
17266             c.events = [];
17267             c.more = [];
17268         });
17269         
17270         Roo.each(this.calevents, function(e) {
17271             Roo.each(e.els, function(el) {
17272                 el.un('mouseenter' ,this.onEventEnter, this);
17273                 el.un('mouseleave' ,this.onEventLeave, this);
17274                 el.remove();
17275             },this);
17276         },this);
17277         
17278         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17279             e.remove();
17280         });
17281         
17282     },
17283     
17284     renderEvents: function()
17285     {   
17286         var _this = this;
17287         
17288         this.cells.each(function(c) {
17289             
17290             if(c.row < 5){
17291                 return;
17292             }
17293             
17294             var ev = c.events;
17295             
17296             var r = 4;
17297             if(c.row != c.events.length){
17298                 r = 4 - (4 - (c.row - c.events.length));
17299             }
17300             
17301             c.events = ev.slice(0, r);
17302             c.more = ev.slice(r);
17303             
17304             if(c.more.length && c.more.length == 1){
17305                 c.events.push(c.more.pop());
17306             }
17307             
17308             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17309             
17310         });
17311             
17312         this.cells.each(function(c) {
17313             
17314             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17315             
17316             
17317             for (var e = 0; e < c.events.length; e++){
17318                 var ev = c.events[e];
17319                 var rows = ev.rows;
17320                 
17321                 for(var i = 0; i < rows.length; i++) {
17322                 
17323                     // how many rows should it span..
17324
17325                     var  cfg = {
17326                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17327                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17328
17329                         unselectable : "on",
17330                         cn : [
17331                             {
17332                                 cls: 'fc-event-inner',
17333                                 cn : [
17334     //                                {
17335     //                                  tag:'span',
17336     //                                  cls: 'fc-event-time',
17337     //                                  html : cells.length > 1 ? '' : ev.time
17338     //                                },
17339                                     {
17340                                       tag:'span',
17341                                       cls: 'fc-event-title',
17342                                       html : String.format('{0}', ev.title)
17343                                     }
17344
17345
17346                                 ]
17347                             },
17348                             {
17349                                 cls: 'ui-resizable-handle ui-resizable-e',
17350                                 html : '&nbsp;&nbsp;&nbsp'
17351                             }
17352
17353                         ]
17354                     };
17355
17356                     if (i == 0) {
17357                         cfg.cls += ' fc-event-start';
17358                     }
17359                     if ((i+1) == rows.length) {
17360                         cfg.cls += ' fc-event-end';
17361                     }
17362
17363                     var ctr = _this.el.select('.fc-event-container',true).first();
17364                     var cg = ctr.createChild(cfg);
17365
17366                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17367                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17368
17369                     var r = (c.more.length) ? 1 : 0;
17370                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17371                     cg.setWidth(ebox.right - sbox.x -2);
17372
17373                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17374                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17375                     cg.on('click', _this.onEventClick, _this, ev);
17376
17377                     ev.els.push(cg);
17378                     
17379                 }
17380                 
17381             }
17382             
17383             
17384             if(c.more.length){
17385                 var  cfg = {
17386                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17387                     style : 'position: absolute',
17388                     unselectable : "on",
17389                     cn : [
17390                         {
17391                             cls: 'fc-event-inner',
17392                             cn : [
17393                                 {
17394                                   tag:'span',
17395                                   cls: 'fc-event-title',
17396                                   html : 'More'
17397                                 }
17398
17399
17400                             ]
17401                         },
17402                         {
17403                             cls: 'ui-resizable-handle ui-resizable-e',
17404                             html : '&nbsp;&nbsp;&nbsp'
17405                         }
17406
17407                     ]
17408                 };
17409
17410                 var ctr = _this.el.select('.fc-event-container',true).first();
17411                 var cg = ctr.createChild(cfg);
17412
17413                 var sbox = c.select('.fc-day-content',true).first().getBox();
17414                 var ebox = c.select('.fc-day-content',true).first().getBox();
17415                 //Roo.log(cg);
17416                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17417                 cg.setWidth(ebox.right - sbox.x -2);
17418
17419                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17420                 
17421             }
17422             
17423         });
17424         
17425         
17426         
17427     },
17428     
17429     onEventEnter: function (e, el,event,d) {
17430         this.fireEvent('evententer', this, el, event);
17431     },
17432     
17433     onEventLeave: function (e, el,event,d) {
17434         this.fireEvent('eventleave', this, el, event);
17435     },
17436     
17437     onEventClick: function (e, el,event,d) {
17438         this.fireEvent('eventclick', this, el, event);
17439     },
17440     
17441     onMonthChange: function () {
17442         this.store.load();
17443     },
17444     
17445     onMoreEventClick: function(e, el, more)
17446     {
17447         var _this = this;
17448         
17449         this.calpopover.placement = 'right';
17450         this.calpopover.setTitle('More');
17451         
17452         this.calpopover.setContent('');
17453         
17454         var ctr = this.calpopover.el.select('.popover-content', true).first();
17455         
17456         Roo.each(more, function(m){
17457             var cfg = {
17458                 cls : 'fc-event-hori fc-event-draggable',
17459                 html : m.title
17460             };
17461             var cg = ctr.createChild(cfg);
17462             
17463             cg.on('click', _this.onEventClick, _this, m);
17464         });
17465         
17466         this.calpopover.show(el);
17467         
17468         
17469     },
17470     
17471     onLoad: function () 
17472     {   
17473         this.calevents = [];
17474         var cal = this;
17475         
17476         if(this.store.getCount() > 0){
17477             this.store.data.each(function(d){
17478                cal.addItem({
17479                     id : d.data.id,
17480                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17481                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17482                     time : d.data.start_time,
17483                     title : d.data.title,
17484                     description : d.data.description,
17485                     venue : d.data.venue
17486                 });
17487             });
17488         }
17489         
17490         this.renderEvents();
17491         
17492         if(this.calevents.length && this.loadMask){
17493             this.maskEl.hide();
17494         }
17495     },
17496     
17497     onBeforeLoad: function()
17498     {
17499         this.clearEvents();
17500         if(this.loadMask){
17501             this.maskEl.show();
17502         }
17503     }
17504 });
17505
17506  
17507  /*
17508  * - LGPL
17509  *
17510  * element
17511  * 
17512  */
17513
17514 /**
17515  * @class Roo.bootstrap.Popover
17516  * @extends Roo.bootstrap.Component
17517  * Bootstrap Popover class
17518  * @cfg {String} html contents of the popover   (or false to use children..)
17519  * @cfg {String} title of popover (or false to hide)
17520  * @cfg {String} placement how it is placed
17521  * @cfg {String} trigger click || hover (or false to trigger manually)
17522  * @cfg {String} over what (parent or false to trigger manually.)
17523  * @cfg {Number} delay - delay before showing
17524  
17525  * @constructor
17526  * Create a new Popover
17527  * @param {Object} config The config object
17528  */
17529
17530 Roo.bootstrap.Popover = function(config){
17531     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17532     
17533     this.addEvents({
17534         // raw events
17535          /**
17536          * @event show
17537          * After the popover show
17538          * 
17539          * @param {Roo.bootstrap.Popover} this
17540          */
17541         "show" : true,
17542         /**
17543          * @event hide
17544          * After the popover hide
17545          * 
17546          * @param {Roo.bootstrap.Popover} this
17547          */
17548         "hide" : true
17549     });
17550 };
17551
17552 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17553     
17554     title: 'Fill in a title',
17555     html: false,
17556     
17557     placement : 'right',
17558     trigger : 'hover', // hover
17559     
17560     delay : 0,
17561     
17562     over: 'parent',
17563     
17564     can_build_overlaid : false,
17565     
17566     getChildContainer : function()
17567     {
17568         return this.el.select('.popover-content',true).first();
17569     },
17570     
17571     getAutoCreate : function(){
17572          
17573         var cfg = {
17574            cls : 'popover roo-dynamic',
17575            style: 'display:block',
17576            cn : [
17577                 {
17578                     cls : 'arrow'
17579                 },
17580                 {
17581                     cls : 'popover-inner',
17582                     cn : [
17583                         {
17584                             tag: 'h3',
17585                             cls: 'popover-title',
17586                             html : this.title
17587                         },
17588                         {
17589                             cls : 'popover-content',
17590                             html : this.html
17591                         }
17592                     ]
17593                     
17594                 }
17595            ]
17596         };
17597         
17598         return cfg;
17599     },
17600     setTitle: function(str)
17601     {
17602         this.title = str;
17603         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17604     },
17605     setContent: function(str)
17606     {
17607         this.html = str;
17608         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17609     },
17610     // as it get's added to the bottom of the page.
17611     onRender : function(ct, position)
17612     {
17613         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17614         if(!this.el){
17615             var cfg = Roo.apply({},  this.getAutoCreate());
17616             cfg.id = Roo.id();
17617             
17618             if (this.cls) {
17619                 cfg.cls += ' ' + this.cls;
17620             }
17621             if (this.style) {
17622                 cfg.style = this.style;
17623             }
17624             //Roo.log("adding to ");
17625             this.el = Roo.get(document.body).createChild(cfg, position);
17626 //            Roo.log(this.el);
17627         }
17628         this.initEvents();
17629     },
17630     
17631     initEvents : function()
17632     {
17633         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17634         this.el.enableDisplayMode('block');
17635         this.el.hide();
17636         if (this.over === false) {
17637             return; 
17638         }
17639         if (this.triggers === false) {
17640             return;
17641         }
17642         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17643         var triggers = this.trigger ? this.trigger.split(' ') : [];
17644         Roo.each(triggers, function(trigger) {
17645         
17646             if (trigger == 'click') {
17647                 on_el.on('click', this.toggle, this);
17648             } else if (trigger != 'manual') {
17649                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17650                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17651       
17652                 on_el.on(eventIn  ,this.enter, this);
17653                 on_el.on(eventOut, this.leave, this);
17654             }
17655         }, this);
17656         
17657     },
17658     
17659     
17660     // private
17661     timeout : null,
17662     hoverState : null,
17663     
17664     toggle : function () {
17665         this.hoverState == 'in' ? this.leave() : this.enter();
17666     },
17667     
17668     enter : function () {
17669         
17670         clearTimeout(this.timeout);
17671     
17672         this.hoverState = 'in';
17673     
17674         if (!this.delay || !this.delay.show) {
17675             this.show();
17676             return;
17677         }
17678         var _t = this;
17679         this.timeout = setTimeout(function () {
17680             if (_t.hoverState == 'in') {
17681                 _t.show();
17682             }
17683         }, this.delay.show)
17684     },
17685     
17686     leave : function() {
17687         clearTimeout(this.timeout);
17688     
17689         this.hoverState = 'out';
17690     
17691         if (!this.delay || !this.delay.hide) {
17692             this.hide();
17693             return;
17694         }
17695         var _t = this;
17696         this.timeout = setTimeout(function () {
17697             if (_t.hoverState == 'out') {
17698                 _t.hide();
17699             }
17700         }, this.delay.hide)
17701     },
17702     
17703     show : function (on_el)
17704     {
17705         if (!on_el) {
17706             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17707         }
17708         
17709         // set content.
17710         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17711         if (this.html !== false) {
17712             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17713         }
17714         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17715         if (!this.title.length) {
17716             this.el.select('.popover-title',true).hide();
17717         }
17718         
17719         var placement = typeof this.placement == 'function' ?
17720             this.placement.call(this, this.el, on_el) :
17721             this.placement;
17722             
17723         var autoToken = /\s?auto?\s?/i;
17724         var autoPlace = autoToken.test(placement);
17725         if (autoPlace) {
17726             placement = placement.replace(autoToken, '') || 'top';
17727         }
17728         
17729         //this.el.detach()
17730         //this.el.setXY([0,0]);
17731         this.el.show();
17732         this.el.dom.style.display='block';
17733         this.el.addClass(placement);
17734         
17735         //this.el.appendTo(on_el);
17736         
17737         var p = this.getPosition();
17738         var box = this.el.getBox();
17739         
17740         if (autoPlace) {
17741             // fixme..
17742         }
17743         var align = Roo.bootstrap.Popover.alignment[placement];
17744         
17745 //        Roo.log(align);
17746         this.el.alignTo(on_el, align[0],align[1]);
17747         //var arrow = this.el.select('.arrow',true).first();
17748         //arrow.set(align[2], 
17749         
17750         this.el.addClass('in');
17751         
17752         
17753         if (this.el.hasClass('fade')) {
17754             // fade it?
17755         }
17756         
17757         this.hoverState = 'in';
17758         
17759         this.fireEvent('show', this);
17760         
17761     },
17762     hide : function()
17763     {
17764         this.el.setXY([0,0]);
17765         this.el.removeClass('in');
17766         this.el.hide();
17767         this.hoverState = null;
17768         
17769         this.fireEvent('hide', this);
17770     }
17771     
17772 });
17773
17774 Roo.bootstrap.Popover.alignment = {
17775     'left' : ['r-l', [-10,0], 'right'],
17776     'right' : ['l-r', [10,0], 'left'],
17777     'bottom' : ['t-b', [0,10], 'top'],
17778     'top' : [ 'b-t', [0,-10], 'bottom']
17779 };
17780
17781  /*
17782  * - LGPL
17783  *
17784  * Progress
17785  * 
17786  */
17787
17788 /**
17789  * @class Roo.bootstrap.Progress
17790  * @extends Roo.bootstrap.Component
17791  * Bootstrap Progress class
17792  * @cfg {Boolean} striped striped of the progress bar
17793  * @cfg {Boolean} active animated of the progress bar
17794  * 
17795  * 
17796  * @constructor
17797  * Create a new Progress
17798  * @param {Object} config The config object
17799  */
17800
17801 Roo.bootstrap.Progress = function(config){
17802     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17803 };
17804
17805 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17806     
17807     striped : false,
17808     active: false,
17809     
17810     getAutoCreate : function(){
17811         var cfg = {
17812             tag: 'div',
17813             cls: 'progress'
17814         };
17815         
17816         
17817         if(this.striped){
17818             cfg.cls += ' progress-striped';
17819         }
17820       
17821         if(this.active){
17822             cfg.cls += ' active';
17823         }
17824         
17825         
17826         return cfg;
17827     }
17828    
17829 });
17830
17831  
17832
17833  /*
17834  * - LGPL
17835  *
17836  * ProgressBar
17837  * 
17838  */
17839
17840 /**
17841  * @class Roo.bootstrap.ProgressBar
17842  * @extends Roo.bootstrap.Component
17843  * Bootstrap ProgressBar class
17844  * @cfg {Number} aria_valuenow aria-value now
17845  * @cfg {Number} aria_valuemin aria-value min
17846  * @cfg {Number} aria_valuemax aria-value max
17847  * @cfg {String} label label for the progress bar
17848  * @cfg {String} panel (success | info | warning | danger )
17849  * @cfg {String} role role of the progress bar
17850  * @cfg {String} sr_only text
17851  * 
17852  * 
17853  * @constructor
17854  * Create a new ProgressBar
17855  * @param {Object} config The config object
17856  */
17857
17858 Roo.bootstrap.ProgressBar = function(config){
17859     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17860 };
17861
17862 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17863     
17864     aria_valuenow : 0,
17865     aria_valuemin : 0,
17866     aria_valuemax : 100,
17867     label : false,
17868     panel : false,
17869     role : false,
17870     sr_only: false,
17871     
17872     getAutoCreate : function()
17873     {
17874         
17875         var cfg = {
17876             tag: 'div',
17877             cls: 'progress-bar',
17878             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17879         };
17880         
17881         if(this.sr_only){
17882             cfg.cn = {
17883                 tag: 'span',
17884                 cls: 'sr-only',
17885                 html: this.sr_only
17886             }
17887         }
17888         
17889         if(this.role){
17890             cfg.role = this.role;
17891         }
17892         
17893         if(this.aria_valuenow){
17894             cfg['aria-valuenow'] = this.aria_valuenow;
17895         }
17896         
17897         if(this.aria_valuemin){
17898             cfg['aria-valuemin'] = this.aria_valuemin;
17899         }
17900         
17901         if(this.aria_valuemax){
17902             cfg['aria-valuemax'] = this.aria_valuemax;
17903         }
17904         
17905         if(this.label && !this.sr_only){
17906             cfg.html = this.label;
17907         }
17908         
17909         if(this.panel){
17910             cfg.cls += ' progress-bar-' + this.panel;
17911         }
17912         
17913         return cfg;
17914     },
17915     
17916     update : function(aria_valuenow)
17917     {
17918         this.aria_valuenow = aria_valuenow;
17919         
17920         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17921     }
17922    
17923 });
17924
17925  
17926
17927  /*
17928  * - LGPL
17929  *
17930  * column
17931  * 
17932  */
17933
17934 /**
17935  * @class Roo.bootstrap.TabGroup
17936  * @extends Roo.bootstrap.Column
17937  * Bootstrap Column class
17938  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17939  * @cfg {Boolean} carousel true to make the group behave like a carousel
17940  * @cfg {Boolean} bullets show bullets for the panels
17941  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17942  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17943  * @cfg {Boolean} showarrow (true|false) show arrow default true
17944  * 
17945  * @constructor
17946  * Create a new TabGroup
17947  * @param {Object} config The config object
17948  */
17949
17950 Roo.bootstrap.TabGroup = function(config){
17951     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17952     if (!this.navId) {
17953         this.navId = Roo.id();
17954     }
17955     this.tabs = [];
17956     Roo.bootstrap.TabGroup.register(this);
17957     
17958 };
17959
17960 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17961     
17962     carousel : false,
17963     transition : false,
17964     bullets : 0,
17965     timer : 0,
17966     autoslide : false,
17967     slideFn : false,
17968     slideOnTouch : false,
17969     showarrow : true,
17970     
17971     getAutoCreate : function()
17972     {
17973         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17974         
17975         cfg.cls += ' tab-content';
17976         
17977         if (this.carousel) {
17978             cfg.cls += ' carousel slide';
17979             
17980             cfg.cn = [{
17981                cls : 'carousel-inner',
17982                cn : []
17983             }];
17984         
17985             if(this.bullets  && !Roo.isTouch){
17986                 
17987                 var bullets = {
17988                     cls : 'carousel-bullets',
17989                     cn : []
17990                 };
17991                
17992                 if(this.bullets_cls){
17993                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17994                 }
17995                 
17996                 bullets.cn.push({
17997                     cls : 'clear'
17998                 });
17999                 
18000                 cfg.cn[0].cn.push(bullets);
18001             }
18002             
18003             if(this.showarrow){
18004                 cfg.cn[0].cn.push({
18005                     tag : 'div',
18006                     class : 'carousel-arrow',
18007                     cn : [
18008                         {
18009                             tag : 'div',
18010                             class : 'carousel-prev',
18011                             cn : [
18012                                 {
18013                                     tag : 'i',
18014                                     class : 'fa fa-chevron-left'
18015                                 }
18016                             ]
18017                         },
18018                         {
18019                             tag : 'div',
18020                             class : 'carousel-next',
18021                             cn : [
18022                                 {
18023                                     tag : 'i',
18024                                     class : 'fa fa-chevron-right'
18025                                 }
18026                             ]
18027                         }
18028                     ]
18029                 });
18030             }
18031             
18032         }
18033         
18034         return cfg;
18035     },
18036     
18037     initEvents:  function()
18038     {
18039 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18040 //            this.el.on("touchstart", this.onTouchStart, this);
18041 //        }
18042         
18043         if(this.autoslide){
18044             var _this = this;
18045             
18046             this.slideFn = window.setInterval(function() {
18047                 _this.showPanelNext();
18048             }, this.timer);
18049         }
18050         
18051         if(this.showarrow){
18052             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18053             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18054         }
18055         
18056         
18057     },
18058     
18059 //    onTouchStart : function(e, el, o)
18060 //    {
18061 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18062 //            return;
18063 //        }
18064 //        
18065 //        this.showPanelNext();
18066 //    },
18067     
18068     
18069     getChildContainer : function()
18070     {
18071         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18072     },
18073     
18074     /**
18075     * register a Navigation item
18076     * @param {Roo.bootstrap.NavItem} the navitem to add
18077     */
18078     register : function(item)
18079     {
18080         this.tabs.push( item);
18081         item.navId = this.navId; // not really needed..
18082         this.addBullet();
18083     
18084     },
18085     
18086     getActivePanel : function()
18087     {
18088         var r = false;
18089         Roo.each(this.tabs, function(t) {
18090             if (t.active) {
18091                 r = t;
18092                 return false;
18093             }
18094             return null;
18095         });
18096         return r;
18097         
18098     },
18099     getPanelByName : function(n)
18100     {
18101         var r = false;
18102         Roo.each(this.tabs, function(t) {
18103             if (t.tabId == n) {
18104                 r = t;
18105                 return false;
18106             }
18107             return null;
18108         });
18109         return r;
18110     },
18111     indexOfPanel : function(p)
18112     {
18113         var r = false;
18114         Roo.each(this.tabs, function(t,i) {
18115             if (t.tabId == p.tabId) {
18116                 r = i;
18117                 return false;
18118             }
18119             return null;
18120         });
18121         return r;
18122     },
18123     /**
18124      * show a specific panel
18125      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18126      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18127      */
18128     showPanel : function (pan)
18129     {
18130         if(this.transition || typeof(pan) == 'undefined'){
18131             Roo.log("waiting for the transitionend");
18132             return;
18133         }
18134         
18135         if (typeof(pan) == 'number') {
18136             pan = this.tabs[pan];
18137         }
18138         
18139         if (typeof(pan) == 'string') {
18140             pan = this.getPanelByName(pan);
18141         }
18142         
18143         var cur = this.getActivePanel();
18144         
18145         if(!pan || !cur){
18146             Roo.log('pan or acitve pan is undefined');
18147             return false;
18148         }
18149         
18150         if (pan.tabId == this.getActivePanel().tabId) {
18151             return true;
18152         }
18153         
18154         if (false === cur.fireEvent('beforedeactivate')) {
18155             return false;
18156         }
18157         
18158         if(this.bullets > 0 && !Roo.isTouch){
18159             this.setActiveBullet(this.indexOfPanel(pan));
18160         }
18161         
18162         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18163             
18164             this.transition = true;
18165             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18166             var lr = dir == 'next' ? 'left' : 'right';
18167             pan.el.addClass(dir); // or prev
18168             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18169             cur.el.addClass(lr); // or right
18170             pan.el.addClass(lr);
18171             
18172             var _this = this;
18173             cur.el.on('transitionend', function() {
18174                 Roo.log("trans end?");
18175                 
18176                 pan.el.removeClass([lr,dir]);
18177                 pan.setActive(true);
18178                 
18179                 cur.el.removeClass([lr]);
18180                 cur.setActive(false);
18181                 
18182                 _this.transition = false;
18183                 
18184             }, this, { single:  true } );
18185             
18186             return true;
18187         }
18188         
18189         cur.setActive(false);
18190         pan.setActive(true);
18191         
18192         return true;
18193         
18194     },
18195     showPanelNext : function()
18196     {
18197         var i = this.indexOfPanel(this.getActivePanel());
18198         
18199         if (i >= this.tabs.length - 1 && !this.autoslide) {
18200             return;
18201         }
18202         
18203         if (i >= this.tabs.length - 1 && this.autoslide) {
18204             i = -1;
18205         }
18206         
18207         this.showPanel(this.tabs[i+1]);
18208     },
18209     
18210     showPanelPrev : function()
18211     {
18212         var i = this.indexOfPanel(this.getActivePanel());
18213         
18214         if (i  < 1 && !this.autoslide) {
18215             return;
18216         }
18217         
18218         if (i < 1 && this.autoslide) {
18219             i = this.tabs.length;
18220         }
18221         
18222         this.showPanel(this.tabs[i-1]);
18223     },
18224     
18225     
18226     addBullet: function()
18227     {
18228         if(!this.bullets || Roo.isTouch){
18229             return;
18230         }
18231         var ctr = this.el.select('.carousel-bullets',true).first();
18232         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18233         var bullet = ctr.createChild({
18234             cls : 'bullet bullet-' + i
18235         },ctr.dom.lastChild);
18236         
18237         
18238         var _this = this;
18239         
18240         bullet.on('click', (function(e, el, o, ii, t){
18241
18242             e.preventDefault();
18243
18244             this.showPanel(ii);
18245
18246             if(this.autoslide && this.slideFn){
18247                 clearInterval(this.slideFn);
18248                 this.slideFn = window.setInterval(function() {
18249                     _this.showPanelNext();
18250                 }, this.timer);
18251             }
18252
18253         }).createDelegate(this, [i, bullet], true));
18254                 
18255         
18256     },
18257      
18258     setActiveBullet : function(i)
18259     {
18260         if(Roo.isTouch){
18261             return;
18262         }
18263         
18264         Roo.each(this.el.select('.bullet', true).elements, function(el){
18265             el.removeClass('selected');
18266         });
18267
18268         var bullet = this.el.select('.bullet-' + i, true).first();
18269         
18270         if(!bullet){
18271             return;
18272         }
18273         
18274         bullet.addClass('selected');
18275     }
18276     
18277     
18278   
18279 });
18280
18281  
18282
18283  
18284  
18285 Roo.apply(Roo.bootstrap.TabGroup, {
18286     
18287     groups: {},
18288      /**
18289     * register a Navigation Group
18290     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18291     */
18292     register : function(navgrp)
18293     {
18294         this.groups[navgrp.navId] = navgrp;
18295         
18296     },
18297     /**
18298     * fetch a Navigation Group based on the navigation ID
18299     * if one does not exist , it will get created.
18300     * @param {string} the navgroup to add
18301     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18302     */
18303     get: function(navId) {
18304         if (typeof(this.groups[navId]) == 'undefined') {
18305             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18306         }
18307         return this.groups[navId] ;
18308     }
18309     
18310     
18311     
18312 });
18313
18314  /*
18315  * - LGPL
18316  *
18317  * TabPanel
18318  * 
18319  */
18320
18321 /**
18322  * @class Roo.bootstrap.TabPanel
18323  * @extends Roo.bootstrap.Component
18324  * Bootstrap TabPanel class
18325  * @cfg {Boolean} active panel active
18326  * @cfg {String} html panel content
18327  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18328  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18329  * @cfg {String} href click to link..
18330  * 
18331  * 
18332  * @constructor
18333  * Create a new TabPanel
18334  * @param {Object} config The config object
18335  */
18336
18337 Roo.bootstrap.TabPanel = function(config){
18338     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18339     this.addEvents({
18340         /**
18341              * @event changed
18342              * Fires when the active status changes
18343              * @param {Roo.bootstrap.TabPanel} this
18344              * @param {Boolean} state the new state
18345             
18346          */
18347         'changed': true,
18348         /**
18349              * @event beforedeactivate
18350              * Fires before a tab is de-activated - can be used to do validation on a form.
18351              * @param {Roo.bootstrap.TabPanel} this
18352              * @return {Boolean} false if there is an error
18353             
18354          */
18355         'beforedeactivate': true
18356      });
18357     
18358     this.tabId = this.tabId || Roo.id();
18359   
18360 };
18361
18362 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18363     
18364     active: false,
18365     html: false,
18366     tabId: false,
18367     navId : false,
18368     href : '',
18369     
18370     getAutoCreate : function(){
18371         var cfg = {
18372             tag: 'div',
18373             // item is needed for carousel - not sure if it has any effect otherwise
18374             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18375             html: this.html || ''
18376         };
18377         
18378         if(this.active){
18379             cfg.cls += ' active';
18380         }
18381         
18382         if(this.tabId){
18383             cfg.tabId = this.tabId;
18384         }
18385         
18386         
18387         return cfg;
18388     },
18389     
18390     initEvents:  function()
18391     {
18392         var p = this.parent();
18393         
18394         this.navId = this.navId || p.navId;
18395         
18396         if (typeof(this.navId) != 'undefined') {
18397             // not really needed.. but just in case.. parent should be a NavGroup.
18398             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18399             
18400             tg.register(this);
18401             
18402             var i = tg.tabs.length - 1;
18403             
18404             if(this.active && tg.bullets > 0 && i < tg.bullets){
18405                 tg.setActiveBullet(i);
18406             }
18407         }
18408         
18409         this.el.on('click', this.onClick, this);
18410         
18411         if(Roo.isTouch){
18412             this.el.on("touchstart", this.onTouchStart, this);
18413             this.el.on("touchmove", this.onTouchMove, this);
18414             this.el.on("touchend", this.onTouchEnd, this);
18415         }
18416         
18417     },
18418     
18419     onRender : function(ct, position)
18420     {
18421         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18422     },
18423     
18424     setActive : function(state)
18425     {
18426         Roo.log("panel - set active " + this.tabId + "=" + state);
18427         
18428         this.active = state;
18429         if (!state) {
18430             this.el.removeClass('active');
18431             
18432         } else  if (!this.el.hasClass('active')) {
18433             this.el.addClass('active');
18434         }
18435         
18436         this.fireEvent('changed', this, state);
18437     },
18438     
18439     onClick : function(e)
18440     {
18441         e.preventDefault();
18442         
18443         if(!this.href.length){
18444             return;
18445         }
18446         
18447         window.location.href = this.href;
18448     },
18449     
18450     startX : 0,
18451     startY : 0,
18452     endX : 0,
18453     endY : 0,
18454     swiping : false,
18455     
18456     onTouchStart : function(e)
18457     {
18458         this.swiping = false;
18459         
18460         this.startX = e.browserEvent.touches[0].clientX;
18461         this.startY = e.browserEvent.touches[0].clientY;
18462     },
18463     
18464     onTouchMove : function(e)
18465     {
18466         this.swiping = true;
18467         
18468         this.endX = e.browserEvent.touches[0].clientX;
18469         this.endY = e.browserEvent.touches[0].clientY;
18470     },
18471     
18472     onTouchEnd : function(e)
18473     {
18474         if(!this.swiping){
18475             this.onClick(e);
18476             return;
18477         }
18478         
18479         var tabGroup = this.parent();
18480         
18481         if(this.endX > this.startX){ // swiping right
18482             tabGroup.showPanelPrev();
18483             return;
18484         }
18485         
18486         if(this.startX > this.endX){ // swiping left
18487             tabGroup.showPanelNext();
18488             return;
18489         }
18490     }
18491     
18492     
18493 });
18494  
18495
18496  
18497
18498  /*
18499  * - LGPL
18500  *
18501  * DateField
18502  * 
18503  */
18504
18505 /**
18506  * @class Roo.bootstrap.DateField
18507  * @extends Roo.bootstrap.Input
18508  * Bootstrap DateField class
18509  * @cfg {Number} weekStart default 0
18510  * @cfg {String} viewMode default empty, (months|years)
18511  * @cfg {String} minViewMode default empty, (months|years)
18512  * @cfg {Number} startDate default -Infinity
18513  * @cfg {Number} endDate default Infinity
18514  * @cfg {Boolean} todayHighlight default false
18515  * @cfg {Boolean} todayBtn default false
18516  * @cfg {Boolean} calendarWeeks default false
18517  * @cfg {Object} daysOfWeekDisabled default empty
18518  * @cfg {Boolean} singleMode default false (true | false)
18519  * 
18520  * @cfg {Boolean} keyboardNavigation default true
18521  * @cfg {String} language default en
18522  * 
18523  * @constructor
18524  * Create a new DateField
18525  * @param {Object} config The config object
18526  */
18527
18528 Roo.bootstrap.DateField = function(config){
18529     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18530      this.addEvents({
18531             /**
18532              * @event show
18533              * Fires when this field show.
18534              * @param {Roo.bootstrap.DateField} this
18535              * @param {Mixed} date The date value
18536              */
18537             show : true,
18538             /**
18539              * @event show
18540              * Fires when this field hide.
18541              * @param {Roo.bootstrap.DateField} this
18542              * @param {Mixed} date The date value
18543              */
18544             hide : true,
18545             /**
18546              * @event select
18547              * Fires when select a date.
18548              * @param {Roo.bootstrap.DateField} this
18549              * @param {Mixed} date The date value
18550              */
18551             select : true,
18552             /**
18553              * @event beforeselect
18554              * Fires when before select a date.
18555              * @param {Roo.bootstrap.DateField} this
18556              * @param {Mixed} date The date value
18557              */
18558             beforeselect : true
18559         });
18560 };
18561
18562 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18563     
18564     /**
18565      * @cfg {String} format
18566      * The default date format string which can be overriden for localization support.  The format must be
18567      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18568      */
18569     format : "m/d/y",
18570     /**
18571      * @cfg {String} altFormats
18572      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18573      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18574      */
18575     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18576     
18577     weekStart : 0,
18578     
18579     viewMode : '',
18580     
18581     minViewMode : '',
18582     
18583     todayHighlight : false,
18584     
18585     todayBtn: false,
18586     
18587     language: 'en',
18588     
18589     keyboardNavigation: true,
18590     
18591     calendarWeeks: false,
18592     
18593     startDate: -Infinity,
18594     
18595     endDate: Infinity,
18596     
18597     daysOfWeekDisabled: [],
18598     
18599     _events: [],
18600     
18601     singleMode : false,
18602     
18603     UTCDate: function()
18604     {
18605         return new Date(Date.UTC.apply(Date, arguments));
18606     },
18607     
18608     UTCToday: function()
18609     {
18610         var today = new Date();
18611         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18612     },
18613     
18614     getDate: function() {
18615             var d = this.getUTCDate();
18616             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18617     },
18618     
18619     getUTCDate: function() {
18620             return this.date;
18621     },
18622     
18623     setDate: function(d) {
18624             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18625     },
18626     
18627     setUTCDate: function(d) {
18628             this.date = d;
18629             this.setValue(this.formatDate(this.date));
18630     },
18631         
18632     onRender: function(ct, position)
18633     {
18634         
18635         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18636         
18637         this.language = this.language || 'en';
18638         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18639         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18640         
18641         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18642         this.format = this.format || 'm/d/y';
18643         this.isInline = false;
18644         this.isInput = true;
18645         this.component = this.el.select('.add-on', true).first() || false;
18646         this.component = (this.component && this.component.length === 0) ? false : this.component;
18647         this.hasInput = this.component && this.inputEl().length;
18648         
18649         if (typeof(this.minViewMode === 'string')) {
18650             switch (this.minViewMode) {
18651                 case 'months':
18652                     this.minViewMode = 1;
18653                     break;
18654                 case 'years':
18655                     this.minViewMode = 2;
18656                     break;
18657                 default:
18658                     this.minViewMode = 0;
18659                     break;
18660             }
18661         }
18662         
18663         if (typeof(this.viewMode === 'string')) {
18664             switch (this.viewMode) {
18665                 case 'months':
18666                     this.viewMode = 1;
18667                     break;
18668                 case 'years':
18669                     this.viewMode = 2;
18670                     break;
18671                 default:
18672                     this.viewMode = 0;
18673                     break;
18674             }
18675         }
18676                 
18677         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18678         
18679 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18680         
18681         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18682         
18683         this.picker().on('mousedown', this.onMousedown, this);
18684         this.picker().on('click', this.onClick, this);
18685         
18686         this.picker().addClass('datepicker-dropdown');
18687         
18688         this.startViewMode = this.viewMode;
18689         
18690         if(this.singleMode){
18691             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18692                 v.setVisibilityMode(Roo.Element.DISPLAY);
18693                 v.hide();
18694             });
18695             
18696             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18697                 v.setStyle('width', '189px');
18698             });
18699         }
18700         
18701         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18702             if(!this.calendarWeeks){
18703                 v.remove();
18704                 return;
18705             }
18706             
18707             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18708             v.attr('colspan', function(i, val){
18709                 return parseInt(val) + 1;
18710             });
18711         });
18712                         
18713         
18714         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18715         
18716         this.setStartDate(this.startDate);
18717         this.setEndDate(this.endDate);
18718         
18719         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18720         
18721         this.fillDow();
18722         this.fillMonths();
18723         this.update();
18724         this.showMode();
18725         
18726         if(this.isInline) {
18727             this.showPopup();
18728         }
18729     },
18730     
18731     picker : function()
18732     {
18733         return this.pickerEl;
18734 //        return this.el.select('.datepicker', true).first();
18735     },
18736     
18737     fillDow: function()
18738     {
18739         var dowCnt = this.weekStart;
18740         
18741         var dow = {
18742             tag: 'tr',
18743             cn: [
18744                 
18745             ]
18746         };
18747         
18748         if(this.calendarWeeks){
18749             dow.cn.push({
18750                 tag: 'th',
18751                 cls: 'cw',
18752                 html: '&nbsp;'
18753             })
18754         }
18755         
18756         while (dowCnt < this.weekStart + 7) {
18757             dow.cn.push({
18758                 tag: 'th',
18759                 cls: 'dow',
18760                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18761             });
18762         }
18763         
18764         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18765     },
18766     
18767     fillMonths: function()
18768     {    
18769         var i = 0;
18770         var months = this.picker().select('>.datepicker-months td', true).first();
18771         
18772         months.dom.innerHTML = '';
18773         
18774         while (i < 12) {
18775             var month = {
18776                 tag: 'span',
18777                 cls: 'month',
18778                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18779             };
18780             
18781             months.createChild(month);
18782         }
18783         
18784     },
18785     
18786     update: function()
18787     {
18788         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;
18789         
18790         if (this.date < this.startDate) {
18791             this.viewDate = new Date(this.startDate);
18792         } else if (this.date > this.endDate) {
18793             this.viewDate = new Date(this.endDate);
18794         } else {
18795             this.viewDate = new Date(this.date);
18796         }
18797         
18798         this.fill();
18799     },
18800     
18801     fill: function() 
18802     {
18803         var d = new Date(this.viewDate),
18804                 year = d.getUTCFullYear(),
18805                 month = d.getUTCMonth(),
18806                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18807                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18808                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18809                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18810                 currentDate = this.date && this.date.valueOf(),
18811                 today = this.UTCToday();
18812         
18813         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18814         
18815 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18816         
18817 //        this.picker.select('>tfoot th.today').
18818 //                                              .text(dates[this.language].today)
18819 //                                              .toggle(this.todayBtn !== false);
18820     
18821         this.updateNavArrows();
18822         this.fillMonths();
18823                                                 
18824         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18825         
18826         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18827          
18828         prevMonth.setUTCDate(day);
18829         
18830         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18831         
18832         var nextMonth = new Date(prevMonth);
18833         
18834         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18835         
18836         nextMonth = nextMonth.valueOf();
18837         
18838         var fillMonths = false;
18839         
18840         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18841         
18842         while(prevMonth.valueOf() <= nextMonth) {
18843             var clsName = '';
18844             
18845             if (prevMonth.getUTCDay() === this.weekStart) {
18846                 if(fillMonths){
18847                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18848                 }
18849                     
18850                 fillMonths = {
18851                     tag: 'tr',
18852                     cn: []
18853                 };
18854                 
18855                 if(this.calendarWeeks){
18856                     // ISO 8601: First week contains first thursday.
18857                     // ISO also states week starts on Monday, but we can be more abstract here.
18858                     var
18859                     // Start of current week: based on weekstart/current date
18860                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18861                     // Thursday of this week
18862                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18863                     // First Thursday of year, year from thursday
18864                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18865                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18866                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18867                     
18868                     fillMonths.cn.push({
18869                         tag: 'td',
18870                         cls: 'cw',
18871                         html: calWeek
18872                     });
18873                 }
18874             }
18875             
18876             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18877                 clsName += ' old';
18878             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18879                 clsName += ' new';
18880             }
18881             if (this.todayHighlight &&
18882                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18883                 prevMonth.getUTCMonth() == today.getMonth() &&
18884                 prevMonth.getUTCDate() == today.getDate()) {
18885                 clsName += ' today';
18886             }
18887             
18888             if (currentDate && prevMonth.valueOf() === currentDate) {
18889                 clsName += ' active';
18890             }
18891             
18892             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18893                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18894                     clsName += ' disabled';
18895             }
18896             
18897             fillMonths.cn.push({
18898                 tag: 'td',
18899                 cls: 'day ' + clsName,
18900                 html: prevMonth.getDate()
18901             });
18902             
18903             prevMonth.setDate(prevMonth.getDate()+1);
18904         }
18905           
18906         var currentYear = this.date && this.date.getUTCFullYear();
18907         var currentMonth = this.date && this.date.getUTCMonth();
18908         
18909         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18910         
18911         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18912             v.removeClass('active');
18913             
18914             if(currentYear === year && k === currentMonth){
18915                 v.addClass('active');
18916             }
18917             
18918             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18919                 v.addClass('disabled');
18920             }
18921             
18922         });
18923         
18924         
18925         year = parseInt(year/10, 10) * 10;
18926         
18927         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18928         
18929         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18930         
18931         year -= 1;
18932         for (var i = -1; i < 11; i++) {
18933             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18934                 tag: 'span',
18935                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18936                 html: year
18937             });
18938             
18939             year += 1;
18940         }
18941     },
18942     
18943     showMode: function(dir) 
18944     {
18945         if (dir) {
18946             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18947         }
18948         
18949         Roo.each(this.picker().select('>div',true).elements, function(v){
18950             Roo.log(v);
18951             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18952             v.hide();
18953         });
18954         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18955     },
18956     
18957     place: function()
18958     {
18959         if(this.isInline) {
18960             return;
18961         }
18962         
18963         this.picker().removeClass(['bottom', 'top']);
18964         
18965         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18966             /*
18967              * place to the top of element!
18968              *
18969              */
18970             
18971             this.picker().addClass('top');
18972             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18973             
18974             return;
18975         }
18976         
18977         this.picker().addClass('bottom');
18978         
18979         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18980     },
18981     
18982     parseDate : function(value)
18983     {
18984         if(!value || value instanceof Date){
18985             return value;
18986         }
18987         var v = Date.parseDate(value, this.format);
18988         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18989             v = Date.parseDate(value, 'Y-m-d');
18990         }
18991         if(!v && this.altFormats){
18992             if(!this.altFormatsArray){
18993                 this.altFormatsArray = this.altFormats.split("|");
18994             }
18995             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18996                 v = Date.parseDate(value, this.altFormatsArray[i]);
18997             }
18998         }
18999         return v;
19000     },
19001     
19002     formatDate : function(date, fmt)
19003     {   
19004         return (!date || !(date instanceof Date)) ?
19005         date : date.dateFormat(fmt || this.format);
19006     },
19007     
19008     onFocus : function()
19009     {
19010         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19011         this.showPopup();
19012     },
19013     
19014     onBlur : function()
19015     {
19016         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19017         
19018         var d = this.inputEl().getValue();
19019         
19020         this.setValue(d);
19021                 
19022         this.hidePopup();
19023     },
19024     
19025     showPopup : function()
19026     {
19027         this.picker().show();
19028         this.update();
19029         this.place();
19030         
19031         this.fireEvent('showpopup', this, this.date);
19032     },
19033     
19034     hidePopup : function()
19035     {
19036         if(this.isInline) {
19037             return;
19038         }
19039         this.picker().hide();
19040         this.viewMode = this.startViewMode;
19041         this.showMode();
19042         
19043         this.fireEvent('hidepopup', this, this.date);
19044         
19045     },
19046     
19047     onMousedown: function(e)
19048     {
19049         e.stopPropagation();
19050         e.preventDefault();
19051     },
19052     
19053     keyup: function(e)
19054     {
19055         Roo.bootstrap.DateField.superclass.keyup.call(this);
19056         this.update();
19057     },
19058
19059     setValue: function(v)
19060     {
19061         if(this.fireEvent('beforeselect', this, v) !== false){
19062             var d = new Date(this.parseDate(v) ).clearTime();
19063         
19064             if(isNaN(d.getTime())){
19065                 this.date = this.viewDate = '';
19066                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19067                 return;
19068             }
19069
19070             v = this.formatDate(d);
19071
19072             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19073
19074             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19075
19076             this.update();
19077
19078             this.fireEvent('select', this, this.date);
19079         }
19080     },
19081     
19082     getValue: function()
19083     {
19084         return this.formatDate(this.date);
19085     },
19086     
19087     fireKey: function(e)
19088     {
19089         if (!this.picker().isVisible()){
19090             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19091                 this.showPopup();
19092             }
19093             return;
19094         }
19095         
19096         var dateChanged = false,
19097         dir, day, month,
19098         newDate, newViewDate;
19099         
19100         switch(e.keyCode){
19101             case 27: // escape
19102                 this.hidePopup();
19103                 e.preventDefault();
19104                 break;
19105             case 37: // left
19106             case 39: // right
19107                 if (!this.keyboardNavigation) {
19108                     break;
19109                 }
19110                 dir = e.keyCode == 37 ? -1 : 1;
19111                 
19112                 if (e.ctrlKey){
19113                     newDate = this.moveYear(this.date, dir);
19114                     newViewDate = this.moveYear(this.viewDate, dir);
19115                 } else if (e.shiftKey){
19116                     newDate = this.moveMonth(this.date, dir);
19117                     newViewDate = this.moveMonth(this.viewDate, dir);
19118                 } else {
19119                     newDate = new Date(this.date);
19120                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19121                     newViewDate = new Date(this.viewDate);
19122                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19123                 }
19124                 if (this.dateWithinRange(newDate)){
19125                     this.date = newDate;
19126                     this.viewDate = newViewDate;
19127                     this.setValue(this.formatDate(this.date));
19128 //                    this.update();
19129                     e.preventDefault();
19130                     dateChanged = true;
19131                 }
19132                 break;
19133             case 38: // up
19134             case 40: // down
19135                 if (!this.keyboardNavigation) {
19136                     break;
19137                 }
19138                 dir = e.keyCode == 38 ? -1 : 1;
19139                 if (e.ctrlKey){
19140                     newDate = this.moveYear(this.date, dir);
19141                     newViewDate = this.moveYear(this.viewDate, dir);
19142                 } else if (e.shiftKey){
19143                     newDate = this.moveMonth(this.date, dir);
19144                     newViewDate = this.moveMonth(this.viewDate, dir);
19145                 } else {
19146                     newDate = new Date(this.date);
19147                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19148                     newViewDate = new Date(this.viewDate);
19149                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19150                 }
19151                 if (this.dateWithinRange(newDate)){
19152                     this.date = newDate;
19153                     this.viewDate = newViewDate;
19154                     this.setValue(this.formatDate(this.date));
19155 //                    this.update();
19156                     e.preventDefault();
19157                     dateChanged = true;
19158                 }
19159                 break;
19160             case 13: // enter
19161                 this.setValue(this.formatDate(this.date));
19162                 this.hidePopup();
19163                 e.preventDefault();
19164                 break;
19165             case 9: // tab
19166                 this.setValue(this.formatDate(this.date));
19167                 this.hidePopup();
19168                 break;
19169             case 16: // shift
19170             case 17: // ctrl
19171             case 18: // alt
19172                 break;
19173             default :
19174                 this.hide();
19175                 
19176         }
19177     },
19178     
19179     
19180     onClick: function(e) 
19181     {
19182         e.stopPropagation();
19183         e.preventDefault();
19184         
19185         var target = e.getTarget();
19186         
19187         if(target.nodeName.toLowerCase() === 'i'){
19188             target = Roo.get(target).dom.parentNode;
19189         }
19190         
19191         var nodeName = target.nodeName;
19192         var className = target.className;
19193         var html = target.innerHTML;
19194         //Roo.log(nodeName);
19195         
19196         switch(nodeName.toLowerCase()) {
19197             case 'th':
19198                 switch(className) {
19199                     case 'switch':
19200                         this.showMode(1);
19201                         break;
19202                     case 'prev':
19203                     case 'next':
19204                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19205                         switch(this.viewMode){
19206                                 case 0:
19207                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19208                                         break;
19209                                 case 1:
19210                                 case 2:
19211                                         this.viewDate = this.moveYear(this.viewDate, dir);
19212                                         break;
19213                         }
19214                         this.fill();
19215                         break;
19216                     case 'today':
19217                         var date = new Date();
19218                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19219 //                        this.fill()
19220                         this.setValue(this.formatDate(this.date));
19221                         
19222                         this.hidePopup();
19223                         break;
19224                 }
19225                 break;
19226             case 'span':
19227                 if (className.indexOf('disabled') < 0) {
19228                     this.viewDate.setUTCDate(1);
19229                     if (className.indexOf('month') > -1) {
19230                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19231                     } else {
19232                         var year = parseInt(html, 10) || 0;
19233                         this.viewDate.setUTCFullYear(year);
19234                         
19235                     }
19236                     
19237                     if(this.singleMode){
19238                         this.setValue(this.formatDate(this.viewDate));
19239                         this.hidePopup();
19240                         return;
19241                     }
19242                     
19243                     this.showMode(-1);
19244                     this.fill();
19245                 }
19246                 break;
19247                 
19248             case 'td':
19249                 //Roo.log(className);
19250                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19251                     var day = parseInt(html, 10) || 1;
19252                     var year = this.viewDate.getUTCFullYear(),
19253                         month = this.viewDate.getUTCMonth();
19254
19255                     if (className.indexOf('old') > -1) {
19256                         if(month === 0 ){
19257                             month = 11;
19258                             year -= 1;
19259                         }else{
19260                             month -= 1;
19261                         }
19262                     } else if (className.indexOf('new') > -1) {
19263                         if (month == 11) {
19264                             month = 0;
19265                             year += 1;
19266                         } else {
19267                             month += 1;
19268                         }
19269                     }
19270                     //Roo.log([year,month,day]);
19271                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19272                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19273 //                    this.fill();
19274                     //Roo.log(this.formatDate(this.date));
19275                     this.setValue(this.formatDate(this.date));
19276                     this.hidePopup();
19277                 }
19278                 break;
19279         }
19280     },
19281     
19282     setStartDate: function(startDate)
19283     {
19284         this.startDate = startDate || -Infinity;
19285         if (this.startDate !== -Infinity) {
19286             this.startDate = this.parseDate(this.startDate);
19287         }
19288         this.update();
19289         this.updateNavArrows();
19290     },
19291
19292     setEndDate: function(endDate)
19293     {
19294         this.endDate = endDate || Infinity;
19295         if (this.endDate !== Infinity) {
19296             this.endDate = this.parseDate(this.endDate);
19297         }
19298         this.update();
19299         this.updateNavArrows();
19300     },
19301     
19302     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19303     {
19304         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19305         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19306             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19307         }
19308         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19309             return parseInt(d, 10);
19310         });
19311         this.update();
19312         this.updateNavArrows();
19313     },
19314     
19315     updateNavArrows: function() 
19316     {
19317         if(this.singleMode){
19318             return;
19319         }
19320         
19321         var d = new Date(this.viewDate),
19322         year = d.getUTCFullYear(),
19323         month = d.getUTCMonth();
19324         
19325         Roo.each(this.picker().select('.prev', true).elements, function(v){
19326             v.show();
19327             switch (this.viewMode) {
19328                 case 0:
19329
19330                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19331                         v.hide();
19332                     }
19333                     break;
19334                 case 1:
19335                 case 2:
19336                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19337                         v.hide();
19338                     }
19339                     break;
19340             }
19341         });
19342         
19343         Roo.each(this.picker().select('.next', true).elements, function(v){
19344             v.show();
19345             switch (this.viewMode) {
19346                 case 0:
19347
19348                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19349                         v.hide();
19350                     }
19351                     break;
19352                 case 1:
19353                 case 2:
19354                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19355                         v.hide();
19356                     }
19357                     break;
19358             }
19359         })
19360     },
19361     
19362     moveMonth: function(date, dir)
19363     {
19364         if (!dir) {
19365             return date;
19366         }
19367         var new_date = new Date(date.valueOf()),
19368         day = new_date.getUTCDate(),
19369         month = new_date.getUTCMonth(),
19370         mag = Math.abs(dir),
19371         new_month, test;
19372         dir = dir > 0 ? 1 : -1;
19373         if (mag == 1){
19374             test = dir == -1
19375             // If going back one month, make sure month is not current month
19376             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19377             ? function(){
19378                 return new_date.getUTCMonth() == month;
19379             }
19380             // If going forward one month, make sure month is as expected
19381             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19382             : function(){
19383                 return new_date.getUTCMonth() != new_month;
19384             };
19385             new_month = month + dir;
19386             new_date.setUTCMonth(new_month);
19387             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19388             if (new_month < 0 || new_month > 11) {
19389                 new_month = (new_month + 12) % 12;
19390             }
19391         } else {
19392             // For magnitudes >1, move one month at a time...
19393             for (var i=0; i<mag; i++) {
19394                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19395                 new_date = this.moveMonth(new_date, dir);
19396             }
19397             // ...then reset the day, keeping it in the new month
19398             new_month = new_date.getUTCMonth();
19399             new_date.setUTCDate(day);
19400             test = function(){
19401                 return new_month != new_date.getUTCMonth();
19402             };
19403         }
19404         // Common date-resetting loop -- if date is beyond end of month, make it
19405         // end of month
19406         while (test()){
19407             new_date.setUTCDate(--day);
19408             new_date.setUTCMonth(new_month);
19409         }
19410         return new_date;
19411     },
19412
19413     moveYear: function(date, dir)
19414     {
19415         return this.moveMonth(date, dir*12);
19416     },
19417
19418     dateWithinRange: function(date)
19419     {
19420         return date >= this.startDate && date <= this.endDate;
19421     },
19422
19423     
19424     remove: function() 
19425     {
19426         this.picker().remove();
19427     },
19428     
19429     validateValue : function(value)
19430     {
19431         if(this.getVisibilityEl().hasClass('hidden')){
19432             return true;
19433         }
19434         
19435         if(value.length < 1)  {
19436             if(this.allowBlank){
19437                 return true;
19438             }
19439             return false;
19440         }
19441         
19442         if(value.length < this.minLength){
19443             return false;
19444         }
19445         if(value.length > this.maxLength){
19446             return false;
19447         }
19448         if(this.vtype){
19449             var vt = Roo.form.VTypes;
19450             if(!vt[this.vtype](value, this)){
19451                 return false;
19452             }
19453         }
19454         if(typeof this.validator == "function"){
19455             var msg = this.validator(value);
19456             if(msg !== true){
19457                 return false;
19458             }
19459         }
19460         
19461         if(this.regex && !this.regex.test(value)){
19462             return false;
19463         }
19464         
19465         if(typeof(this.parseDate(value)) == 'undefined'){
19466             return false;
19467         }
19468         
19469         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19470             return false;
19471         }      
19472         
19473         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19474             return false;
19475         } 
19476         
19477         
19478         return true;
19479     },
19480     
19481     reset : function()
19482     {
19483         this.date = this.viewDate = '';
19484         
19485         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19486     }
19487    
19488 });
19489
19490 Roo.apply(Roo.bootstrap.DateField,  {
19491     
19492     head : {
19493         tag: 'thead',
19494         cn: [
19495         {
19496             tag: 'tr',
19497             cn: [
19498             {
19499                 tag: 'th',
19500                 cls: 'prev',
19501                 html: '<i class="fa fa-arrow-left"/>'
19502             },
19503             {
19504                 tag: 'th',
19505                 cls: 'switch',
19506                 colspan: '5'
19507             },
19508             {
19509                 tag: 'th',
19510                 cls: 'next',
19511                 html: '<i class="fa fa-arrow-right"/>'
19512             }
19513
19514             ]
19515         }
19516         ]
19517     },
19518     
19519     content : {
19520         tag: 'tbody',
19521         cn: [
19522         {
19523             tag: 'tr',
19524             cn: [
19525             {
19526                 tag: 'td',
19527                 colspan: '7'
19528             }
19529             ]
19530         }
19531         ]
19532     },
19533     
19534     footer : {
19535         tag: 'tfoot',
19536         cn: [
19537         {
19538             tag: 'tr',
19539             cn: [
19540             {
19541                 tag: 'th',
19542                 colspan: '7',
19543                 cls: 'today'
19544             }
19545                     
19546             ]
19547         }
19548         ]
19549     },
19550     
19551     dates:{
19552         en: {
19553             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19554             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19555             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19556             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19557             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19558             today: "Today"
19559         }
19560     },
19561     
19562     modes: [
19563     {
19564         clsName: 'days',
19565         navFnc: 'Month',
19566         navStep: 1
19567     },
19568     {
19569         clsName: 'months',
19570         navFnc: 'FullYear',
19571         navStep: 1
19572     },
19573     {
19574         clsName: 'years',
19575         navFnc: 'FullYear',
19576         navStep: 10
19577     }]
19578 });
19579
19580 Roo.apply(Roo.bootstrap.DateField,  {
19581   
19582     template : {
19583         tag: 'div',
19584         cls: 'datepicker dropdown-menu roo-dynamic',
19585         cn: [
19586         {
19587             tag: 'div',
19588             cls: 'datepicker-days',
19589             cn: [
19590             {
19591                 tag: 'table',
19592                 cls: 'table-condensed',
19593                 cn:[
19594                 Roo.bootstrap.DateField.head,
19595                 {
19596                     tag: 'tbody'
19597                 },
19598                 Roo.bootstrap.DateField.footer
19599                 ]
19600             }
19601             ]
19602         },
19603         {
19604             tag: 'div',
19605             cls: 'datepicker-months',
19606             cn: [
19607             {
19608                 tag: 'table',
19609                 cls: 'table-condensed',
19610                 cn:[
19611                 Roo.bootstrap.DateField.head,
19612                 Roo.bootstrap.DateField.content,
19613                 Roo.bootstrap.DateField.footer
19614                 ]
19615             }
19616             ]
19617         },
19618         {
19619             tag: 'div',
19620             cls: 'datepicker-years',
19621             cn: [
19622             {
19623                 tag: 'table',
19624                 cls: 'table-condensed',
19625                 cn:[
19626                 Roo.bootstrap.DateField.head,
19627                 Roo.bootstrap.DateField.content,
19628                 Roo.bootstrap.DateField.footer
19629                 ]
19630             }
19631             ]
19632         }
19633         ]
19634     }
19635 });
19636
19637  
19638
19639  /*
19640  * - LGPL
19641  *
19642  * TimeField
19643  * 
19644  */
19645
19646 /**
19647  * @class Roo.bootstrap.TimeField
19648  * @extends Roo.bootstrap.Input
19649  * Bootstrap DateField class
19650  * 
19651  * 
19652  * @constructor
19653  * Create a new TimeField
19654  * @param {Object} config The config object
19655  */
19656
19657 Roo.bootstrap.TimeField = function(config){
19658     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19659     this.addEvents({
19660             /**
19661              * @event show
19662              * Fires when this field show.
19663              * @param {Roo.bootstrap.DateField} thisthis
19664              * @param {Mixed} date The date value
19665              */
19666             show : true,
19667             /**
19668              * @event show
19669              * Fires when this field hide.
19670              * @param {Roo.bootstrap.DateField} this
19671              * @param {Mixed} date The date value
19672              */
19673             hide : true,
19674             /**
19675              * @event select
19676              * Fires when select a date.
19677              * @param {Roo.bootstrap.DateField} this
19678              * @param {Mixed} date The date value
19679              */
19680             select : true
19681         });
19682 };
19683
19684 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19685     
19686     /**
19687      * @cfg {String} format
19688      * The default time format string which can be overriden for localization support.  The format must be
19689      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19690      */
19691     format : "H:i",
19692        
19693     onRender: function(ct, position)
19694     {
19695         
19696         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19697                 
19698         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19699         
19700         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19701         
19702         this.pop = this.picker().select('>.datepicker-time',true).first();
19703         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19704         
19705         this.picker().on('mousedown', this.onMousedown, this);
19706         this.picker().on('click', this.onClick, this);
19707         
19708         this.picker().addClass('datepicker-dropdown');
19709     
19710         this.fillTime();
19711         this.update();
19712             
19713         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19714         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19715         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19716         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19717         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19718         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19719
19720     },
19721     
19722     fireKey: function(e){
19723         if (!this.picker().isVisible()){
19724             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19725                 this.show();
19726             }
19727             return;
19728         }
19729
19730         e.preventDefault();
19731         
19732         switch(e.keyCode){
19733             case 27: // escape
19734                 this.hide();
19735                 break;
19736             case 37: // left
19737             case 39: // right
19738                 this.onTogglePeriod();
19739                 break;
19740             case 38: // up
19741                 this.onIncrementMinutes();
19742                 break;
19743             case 40: // down
19744                 this.onDecrementMinutes();
19745                 break;
19746             case 13: // enter
19747             case 9: // tab
19748                 this.setTime();
19749                 break;
19750         }
19751     },
19752     
19753     onClick: function(e) {
19754         e.stopPropagation();
19755         e.preventDefault();
19756     },
19757     
19758     picker : function()
19759     {
19760         return this.el.select('.datepicker', true).first();
19761     },
19762     
19763     fillTime: function()
19764     {    
19765         var time = this.pop.select('tbody', true).first();
19766         
19767         time.dom.innerHTML = '';
19768         
19769         time.createChild({
19770             tag: 'tr',
19771             cn: [
19772                 {
19773                     tag: 'td',
19774                     cn: [
19775                         {
19776                             tag: 'a',
19777                             href: '#',
19778                             cls: 'btn',
19779                             cn: [
19780                                 {
19781                                     tag: 'span',
19782                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19783                                 }
19784                             ]
19785                         } 
19786                     ]
19787                 },
19788                 {
19789                     tag: 'td',
19790                     cls: 'separator'
19791                 },
19792                 {
19793                     tag: 'td',
19794                     cn: [
19795                         {
19796                             tag: 'a',
19797                             href: '#',
19798                             cls: 'btn',
19799                             cn: [
19800                                 {
19801                                     tag: 'span',
19802                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19803                                 }
19804                             ]
19805                         }
19806                     ]
19807                 },
19808                 {
19809                     tag: 'td',
19810                     cls: 'separator'
19811                 }
19812             ]
19813         });
19814         
19815         time.createChild({
19816             tag: 'tr',
19817             cn: [
19818                 {
19819                     tag: 'td',
19820                     cn: [
19821                         {
19822                             tag: 'span',
19823                             cls: 'timepicker-hour',
19824                             html: '00'
19825                         }  
19826                     ]
19827                 },
19828                 {
19829                     tag: 'td',
19830                     cls: 'separator',
19831                     html: ':'
19832                 },
19833                 {
19834                     tag: 'td',
19835                     cn: [
19836                         {
19837                             tag: 'span',
19838                             cls: 'timepicker-minute',
19839                             html: '00'
19840                         }  
19841                     ]
19842                 },
19843                 {
19844                     tag: 'td',
19845                     cls: 'separator'
19846                 },
19847                 {
19848                     tag: 'td',
19849                     cn: [
19850                         {
19851                             tag: 'button',
19852                             type: 'button',
19853                             cls: 'btn btn-primary period',
19854                             html: 'AM'
19855                             
19856                         }
19857                     ]
19858                 }
19859             ]
19860         });
19861         
19862         time.createChild({
19863             tag: 'tr',
19864             cn: [
19865                 {
19866                     tag: 'td',
19867                     cn: [
19868                         {
19869                             tag: 'a',
19870                             href: '#',
19871                             cls: 'btn',
19872                             cn: [
19873                                 {
19874                                     tag: 'span',
19875                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19876                                 }
19877                             ]
19878                         }
19879                     ]
19880                 },
19881                 {
19882                     tag: 'td',
19883                     cls: 'separator'
19884                 },
19885                 {
19886                     tag: 'td',
19887                     cn: [
19888                         {
19889                             tag: 'a',
19890                             href: '#',
19891                             cls: 'btn',
19892                             cn: [
19893                                 {
19894                                     tag: 'span',
19895                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19896                                 }
19897                             ]
19898                         }
19899                     ]
19900                 },
19901                 {
19902                     tag: 'td',
19903                     cls: 'separator'
19904                 }
19905             ]
19906         });
19907         
19908     },
19909     
19910     update: function()
19911     {
19912         
19913         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19914         
19915         this.fill();
19916     },
19917     
19918     fill: function() 
19919     {
19920         var hours = this.time.getHours();
19921         var minutes = this.time.getMinutes();
19922         var period = 'AM';
19923         
19924         if(hours > 11){
19925             period = 'PM';
19926         }
19927         
19928         if(hours == 0){
19929             hours = 12;
19930         }
19931         
19932         
19933         if(hours > 12){
19934             hours = hours - 12;
19935         }
19936         
19937         if(hours < 10){
19938             hours = '0' + hours;
19939         }
19940         
19941         if(minutes < 10){
19942             minutes = '0' + minutes;
19943         }
19944         
19945         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19946         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19947         this.pop.select('button', true).first().dom.innerHTML = period;
19948         
19949     },
19950     
19951     place: function()
19952     {   
19953         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19954         
19955         var cls = ['bottom'];
19956         
19957         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19958             cls.pop();
19959             cls.push('top');
19960         }
19961         
19962         cls.push('right');
19963         
19964         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19965             cls.pop();
19966             cls.push('left');
19967         }
19968         
19969         this.picker().addClass(cls.join('-'));
19970         
19971         var _this = this;
19972         
19973         Roo.each(cls, function(c){
19974             if(c == 'bottom'){
19975                 _this.picker().setTop(_this.inputEl().getHeight());
19976                 return;
19977             }
19978             if(c == 'top'){
19979                 _this.picker().setTop(0 - _this.picker().getHeight());
19980                 return;
19981             }
19982             
19983             if(c == 'left'){
19984                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19985                 return;
19986             }
19987             if(c == 'right'){
19988                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19989                 return;
19990             }
19991         });
19992         
19993     },
19994   
19995     onFocus : function()
19996     {
19997         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19998         this.show();
19999     },
20000     
20001     onBlur : function()
20002     {
20003         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20004         this.hide();
20005     },
20006     
20007     show : function()
20008     {
20009         this.picker().show();
20010         this.pop.show();
20011         this.update();
20012         this.place();
20013         
20014         this.fireEvent('show', this, this.date);
20015     },
20016     
20017     hide : function()
20018     {
20019         this.picker().hide();
20020         this.pop.hide();
20021         
20022         this.fireEvent('hide', this, this.date);
20023     },
20024     
20025     setTime : function()
20026     {
20027         this.hide();
20028         this.setValue(this.time.format(this.format));
20029         
20030         this.fireEvent('select', this, this.date);
20031         
20032         
20033     },
20034     
20035     onMousedown: function(e){
20036         e.stopPropagation();
20037         e.preventDefault();
20038     },
20039     
20040     onIncrementHours: function()
20041     {
20042         Roo.log('onIncrementHours');
20043         this.time = this.time.add(Date.HOUR, 1);
20044         this.update();
20045         
20046     },
20047     
20048     onDecrementHours: function()
20049     {
20050         Roo.log('onDecrementHours');
20051         this.time = this.time.add(Date.HOUR, -1);
20052         this.update();
20053     },
20054     
20055     onIncrementMinutes: function()
20056     {
20057         Roo.log('onIncrementMinutes');
20058         this.time = this.time.add(Date.MINUTE, 1);
20059         this.update();
20060     },
20061     
20062     onDecrementMinutes: function()
20063     {
20064         Roo.log('onDecrementMinutes');
20065         this.time = this.time.add(Date.MINUTE, -1);
20066         this.update();
20067     },
20068     
20069     onTogglePeriod: function()
20070     {
20071         Roo.log('onTogglePeriod');
20072         this.time = this.time.add(Date.HOUR, 12);
20073         this.update();
20074     }
20075     
20076    
20077 });
20078
20079 Roo.apply(Roo.bootstrap.TimeField,  {
20080     
20081     content : {
20082         tag: 'tbody',
20083         cn: [
20084             {
20085                 tag: 'tr',
20086                 cn: [
20087                 {
20088                     tag: 'td',
20089                     colspan: '7'
20090                 }
20091                 ]
20092             }
20093         ]
20094     },
20095     
20096     footer : {
20097         tag: 'tfoot',
20098         cn: [
20099             {
20100                 tag: 'tr',
20101                 cn: [
20102                 {
20103                     tag: 'th',
20104                     colspan: '7',
20105                     cls: '',
20106                     cn: [
20107                         {
20108                             tag: 'button',
20109                             cls: 'btn btn-info ok',
20110                             html: 'OK'
20111                         }
20112                     ]
20113                 }
20114
20115                 ]
20116             }
20117         ]
20118     }
20119 });
20120
20121 Roo.apply(Roo.bootstrap.TimeField,  {
20122   
20123     template : {
20124         tag: 'div',
20125         cls: 'datepicker dropdown-menu',
20126         cn: [
20127             {
20128                 tag: 'div',
20129                 cls: 'datepicker-time',
20130                 cn: [
20131                 {
20132                     tag: 'table',
20133                     cls: 'table-condensed',
20134                     cn:[
20135                     Roo.bootstrap.TimeField.content,
20136                     Roo.bootstrap.TimeField.footer
20137                     ]
20138                 }
20139                 ]
20140             }
20141         ]
20142     }
20143 });
20144
20145  
20146
20147  /*
20148  * - LGPL
20149  *
20150  * MonthField
20151  * 
20152  */
20153
20154 /**
20155  * @class Roo.bootstrap.MonthField
20156  * @extends Roo.bootstrap.Input
20157  * Bootstrap MonthField class
20158  * 
20159  * @cfg {String} language default en
20160  * 
20161  * @constructor
20162  * Create a new MonthField
20163  * @param {Object} config The config object
20164  */
20165
20166 Roo.bootstrap.MonthField = function(config){
20167     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20168     
20169     this.addEvents({
20170         /**
20171          * @event show
20172          * Fires when this field show.
20173          * @param {Roo.bootstrap.MonthField} this
20174          * @param {Mixed} date The date value
20175          */
20176         show : true,
20177         /**
20178          * @event show
20179          * Fires when this field hide.
20180          * @param {Roo.bootstrap.MonthField} this
20181          * @param {Mixed} date The date value
20182          */
20183         hide : true,
20184         /**
20185          * @event select
20186          * Fires when select a date.
20187          * @param {Roo.bootstrap.MonthField} this
20188          * @param {String} oldvalue The old value
20189          * @param {String} newvalue The new value
20190          */
20191         select : true
20192     });
20193 };
20194
20195 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20196     
20197     onRender: function(ct, position)
20198     {
20199         
20200         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20201         
20202         this.language = this.language || 'en';
20203         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20204         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20205         
20206         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20207         this.isInline = false;
20208         this.isInput = true;
20209         this.component = this.el.select('.add-on', true).first() || false;
20210         this.component = (this.component && this.component.length === 0) ? false : this.component;
20211         this.hasInput = this.component && this.inputEL().length;
20212         
20213         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20214         
20215         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20216         
20217         this.picker().on('mousedown', this.onMousedown, this);
20218         this.picker().on('click', this.onClick, this);
20219         
20220         this.picker().addClass('datepicker-dropdown');
20221         
20222         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20223             v.setStyle('width', '189px');
20224         });
20225         
20226         this.fillMonths();
20227         
20228         this.update();
20229         
20230         if(this.isInline) {
20231             this.show();
20232         }
20233         
20234     },
20235     
20236     setValue: function(v, suppressEvent)
20237     {   
20238         var o = this.getValue();
20239         
20240         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20241         
20242         this.update();
20243
20244         if(suppressEvent !== true){
20245             this.fireEvent('select', this, o, v);
20246         }
20247         
20248     },
20249     
20250     getValue: function()
20251     {
20252         return this.value;
20253     },
20254     
20255     onClick: function(e) 
20256     {
20257         e.stopPropagation();
20258         e.preventDefault();
20259         
20260         var target = e.getTarget();
20261         
20262         if(target.nodeName.toLowerCase() === 'i'){
20263             target = Roo.get(target).dom.parentNode;
20264         }
20265         
20266         var nodeName = target.nodeName;
20267         var className = target.className;
20268         var html = target.innerHTML;
20269         
20270         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20271             return;
20272         }
20273         
20274         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20275         
20276         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20277         
20278         this.hide();
20279                         
20280     },
20281     
20282     picker : function()
20283     {
20284         return this.pickerEl;
20285     },
20286     
20287     fillMonths: function()
20288     {    
20289         var i = 0;
20290         var months = this.picker().select('>.datepicker-months td', true).first();
20291         
20292         months.dom.innerHTML = '';
20293         
20294         while (i < 12) {
20295             var month = {
20296                 tag: 'span',
20297                 cls: 'month',
20298                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20299             };
20300             
20301             months.createChild(month);
20302         }
20303         
20304     },
20305     
20306     update: function()
20307     {
20308         var _this = this;
20309         
20310         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20311             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20312         }
20313         
20314         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20315             e.removeClass('active');
20316             
20317             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20318                 e.addClass('active');
20319             }
20320         })
20321     },
20322     
20323     place: function()
20324     {
20325         if(this.isInline) {
20326             return;
20327         }
20328         
20329         this.picker().removeClass(['bottom', 'top']);
20330         
20331         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20332             /*
20333              * place to the top of element!
20334              *
20335              */
20336             
20337             this.picker().addClass('top');
20338             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20339             
20340             return;
20341         }
20342         
20343         this.picker().addClass('bottom');
20344         
20345         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20346     },
20347     
20348     onFocus : function()
20349     {
20350         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20351         this.show();
20352     },
20353     
20354     onBlur : function()
20355     {
20356         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20357         
20358         var d = this.inputEl().getValue();
20359         
20360         this.setValue(d);
20361                 
20362         this.hide();
20363     },
20364     
20365     show : function()
20366     {
20367         this.picker().show();
20368         this.picker().select('>.datepicker-months', true).first().show();
20369         this.update();
20370         this.place();
20371         
20372         this.fireEvent('show', this, this.date);
20373     },
20374     
20375     hide : function()
20376     {
20377         if(this.isInline) {
20378             return;
20379         }
20380         this.picker().hide();
20381         this.fireEvent('hide', this, this.date);
20382         
20383     },
20384     
20385     onMousedown: function(e)
20386     {
20387         e.stopPropagation();
20388         e.preventDefault();
20389     },
20390     
20391     keyup: function(e)
20392     {
20393         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20394         this.update();
20395     },
20396
20397     fireKey: function(e)
20398     {
20399         if (!this.picker().isVisible()){
20400             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20401                 this.show();
20402             }
20403             return;
20404         }
20405         
20406         var dir;
20407         
20408         switch(e.keyCode){
20409             case 27: // escape
20410                 this.hide();
20411                 e.preventDefault();
20412                 break;
20413             case 37: // left
20414             case 39: // right
20415                 dir = e.keyCode == 37 ? -1 : 1;
20416                 
20417                 this.vIndex = this.vIndex + dir;
20418                 
20419                 if(this.vIndex < 0){
20420                     this.vIndex = 0;
20421                 }
20422                 
20423                 if(this.vIndex > 11){
20424                     this.vIndex = 11;
20425                 }
20426                 
20427                 if(isNaN(this.vIndex)){
20428                     this.vIndex = 0;
20429                 }
20430                 
20431                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20432                 
20433                 break;
20434             case 38: // up
20435             case 40: // down
20436                 
20437                 dir = e.keyCode == 38 ? -1 : 1;
20438                 
20439                 this.vIndex = this.vIndex + dir * 4;
20440                 
20441                 if(this.vIndex < 0){
20442                     this.vIndex = 0;
20443                 }
20444                 
20445                 if(this.vIndex > 11){
20446                     this.vIndex = 11;
20447                 }
20448                 
20449                 if(isNaN(this.vIndex)){
20450                     this.vIndex = 0;
20451                 }
20452                 
20453                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20454                 break;
20455                 
20456             case 13: // enter
20457                 
20458                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20459                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20460                 }
20461                 
20462                 this.hide();
20463                 e.preventDefault();
20464                 break;
20465             case 9: // tab
20466                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20467                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20468                 }
20469                 this.hide();
20470                 break;
20471             case 16: // shift
20472             case 17: // ctrl
20473             case 18: // alt
20474                 break;
20475             default :
20476                 this.hide();
20477                 
20478         }
20479     },
20480     
20481     remove: function() 
20482     {
20483         this.picker().remove();
20484     }
20485    
20486 });
20487
20488 Roo.apply(Roo.bootstrap.MonthField,  {
20489     
20490     content : {
20491         tag: 'tbody',
20492         cn: [
20493         {
20494             tag: 'tr',
20495             cn: [
20496             {
20497                 tag: 'td',
20498                 colspan: '7'
20499             }
20500             ]
20501         }
20502         ]
20503     },
20504     
20505     dates:{
20506         en: {
20507             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20508             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20509         }
20510     }
20511 });
20512
20513 Roo.apply(Roo.bootstrap.MonthField,  {
20514   
20515     template : {
20516         tag: 'div',
20517         cls: 'datepicker dropdown-menu roo-dynamic',
20518         cn: [
20519             {
20520                 tag: 'div',
20521                 cls: 'datepicker-months',
20522                 cn: [
20523                 {
20524                     tag: 'table',
20525                     cls: 'table-condensed',
20526                     cn:[
20527                         Roo.bootstrap.DateField.content
20528                     ]
20529                 }
20530                 ]
20531             }
20532         ]
20533     }
20534 });
20535
20536  
20537
20538  
20539  /*
20540  * - LGPL
20541  *
20542  * CheckBox
20543  * 
20544  */
20545
20546 /**
20547  * @class Roo.bootstrap.CheckBox
20548  * @extends Roo.bootstrap.Input
20549  * Bootstrap CheckBox class
20550  * 
20551  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20552  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20553  * @cfg {String} boxLabel The text that appears beside the checkbox
20554  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20555  * @cfg {Boolean} checked initnal the element
20556  * @cfg {Boolean} inline inline the element (default false)
20557  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20558  * @cfg {String} tooltip label tooltip
20559  * 
20560  * @constructor
20561  * Create a new CheckBox
20562  * @param {Object} config The config object
20563  */
20564
20565 Roo.bootstrap.CheckBox = function(config){
20566     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20567    
20568     this.addEvents({
20569         /**
20570         * @event check
20571         * Fires when the element is checked or unchecked.
20572         * @param {Roo.bootstrap.CheckBox} this This input
20573         * @param {Boolean} checked The new checked value
20574         */
20575        check : true,
20576        /**
20577         * @event click
20578         * Fires when the element is click.
20579         * @param {Roo.bootstrap.CheckBox} this This input
20580         */
20581        click : true
20582     });
20583     
20584 };
20585
20586 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20587   
20588     inputType: 'checkbox',
20589     inputValue: 1,
20590     valueOff: 0,
20591     boxLabel: false,
20592     checked: false,
20593     weight : false,
20594     inline: false,
20595     tooltip : '',
20596     
20597     getAutoCreate : function()
20598     {
20599         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20600         
20601         var id = Roo.id();
20602         
20603         var cfg = {};
20604         
20605         cfg.cls = 'form-group ' + this.inputType; //input-group
20606         
20607         if(this.inline){
20608             cfg.cls += ' ' + this.inputType + '-inline';
20609         }
20610         
20611         var input =  {
20612             tag: 'input',
20613             id : id,
20614             type : this.inputType,
20615             value : this.inputValue,
20616             cls : 'roo-' + this.inputType, //'form-box',
20617             placeholder : this.placeholder || ''
20618             
20619         };
20620         
20621         if(this.inputType != 'radio'){
20622             var hidden =  {
20623                 tag: 'input',
20624                 type : 'hidden',
20625                 cls : 'roo-hidden-value',
20626                 value : this.checked ? this.inputValue : this.valueOff
20627             };
20628         }
20629         
20630             
20631         if (this.weight) { // Validity check?
20632             cfg.cls += " " + this.inputType + "-" + this.weight;
20633         }
20634         
20635         if (this.disabled) {
20636             input.disabled=true;
20637         }
20638         
20639         if(this.checked){
20640             input.checked = this.checked;
20641         }
20642         
20643         if (this.name) {
20644             
20645             input.name = this.name;
20646             
20647             if(this.inputType != 'radio'){
20648                 hidden.name = this.name;
20649                 input.name = '_hidden_' + this.name;
20650             }
20651         }
20652         
20653         if (this.size) {
20654             input.cls += ' input-' + this.size;
20655         }
20656         
20657         var settings=this;
20658         
20659         ['xs','sm','md','lg'].map(function(size){
20660             if (settings[size]) {
20661                 cfg.cls += ' col-' + size + '-' + settings[size];
20662             }
20663         });
20664         
20665         var inputblock = input;
20666          
20667         if (this.before || this.after) {
20668             
20669             inputblock = {
20670                 cls : 'input-group',
20671                 cn :  [] 
20672             };
20673             
20674             if (this.before) {
20675                 inputblock.cn.push({
20676                     tag :'span',
20677                     cls : 'input-group-addon',
20678                     html : this.before
20679                 });
20680             }
20681             
20682             inputblock.cn.push(input);
20683             
20684             if(this.inputType != 'radio'){
20685                 inputblock.cn.push(hidden);
20686             }
20687             
20688             if (this.after) {
20689                 inputblock.cn.push({
20690                     tag :'span',
20691                     cls : 'input-group-addon',
20692                     html : this.after
20693                 });
20694             }
20695             
20696         }
20697         
20698         if (align ==='left' && this.fieldLabel.length) {
20699 //                Roo.log("left and has label");
20700             cfg.cn = [
20701                 {
20702                     tag: 'label',
20703                     'for' :  id,
20704                     cls : 'control-label',
20705                     html : this.fieldLabel
20706                 },
20707                 {
20708                     cls : "", 
20709                     cn: [
20710                         inputblock
20711                     ]
20712                 }
20713             ];
20714             
20715             if(this.labelWidth > 12){
20716                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20717             }
20718             
20719             if(this.labelWidth < 13 && this.labelmd == 0){
20720                 this.labelmd = this.labelWidth;
20721             }
20722             
20723             if(this.labellg > 0){
20724                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20725                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20726             }
20727             
20728             if(this.labelmd > 0){
20729                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20730                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20731             }
20732             
20733             if(this.labelsm > 0){
20734                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20735                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20736             }
20737             
20738             if(this.labelxs > 0){
20739                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20740                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20741             }
20742             
20743         } else if ( this.fieldLabel.length) {
20744 //                Roo.log(" label");
20745                 cfg.cn = [
20746                    
20747                     {
20748                         tag: this.boxLabel ? 'span' : 'label',
20749                         'for': id,
20750                         cls: 'control-label box-input-label',
20751                         //cls : 'input-group-addon',
20752                         html : this.fieldLabel
20753                     },
20754                     
20755                     inputblock
20756                     
20757                 ];
20758
20759         } else {
20760             
20761 //                Roo.log(" no label && no align");
20762                 cfg.cn = [  inputblock ] ;
20763                 
20764                 
20765         }
20766         
20767         if(this.boxLabel){
20768              var boxLabelCfg = {
20769                 tag: 'label',
20770                 //'for': id, // box label is handled by onclick - so no for...
20771                 cls: 'box-label',
20772                 html: this.boxLabel
20773             };
20774             
20775             if(this.tooltip){
20776                 boxLabelCfg.tooltip = this.tooltip;
20777             }
20778              
20779             cfg.cn.push(boxLabelCfg);
20780         }
20781         
20782         if(this.inputType != 'radio'){
20783             cfg.cn.push(hidden);
20784         }
20785         
20786         return cfg;
20787         
20788     },
20789     
20790     /**
20791      * return the real input element.
20792      */
20793     inputEl: function ()
20794     {
20795         return this.el.select('input.roo-' + this.inputType,true).first();
20796     },
20797     hiddenEl: function ()
20798     {
20799         return this.el.select('input.roo-hidden-value',true).first();
20800     },
20801     
20802     labelEl: function()
20803     {
20804         return this.el.select('label.control-label',true).first();
20805     },
20806     /* depricated... */
20807     
20808     label: function()
20809     {
20810         return this.labelEl();
20811     },
20812     
20813     boxLabelEl: function()
20814     {
20815         return this.el.select('label.box-label',true).first();
20816     },
20817     
20818     initEvents : function()
20819     {
20820 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20821         
20822         this.inputEl().on('click', this.onClick,  this);
20823         
20824         if (this.boxLabel) { 
20825             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20826         }
20827         
20828         this.startValue = this.getValue();
20829         
20830         if(this.groupId){
20831             Roo.bootstrap.CheckBox.register(this);
20832         }
20833     },
20834     
20835     onClick : function(e)
20836     {   
20837         if(this.fireEvent('click', this, e) !== false){
20838             this.setChecked(!this.checked);
20839         }
20840         
20841     },
20842     
20843     setChecked : function(state,suppressEvent)
20844     {
20845         this.startValue = this.getValue();
20846
20847         if(this.inputType == 'radio'){
20848             
20849             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20850                 e.dom.checked = false;
20851             });
20852             
20853             this.inputEl().dom.checked = true;
20854             
20855             this.inputEl().dom.value = this.inputValue;
20856             
20857             if(suppressEvent !== true){
20858                 this.fireEvent('check', this, true);
20859             }
20860             
20861             this.validate();
20862             
20863             return;
20864         }
20865         
20866         this.checked = state;
20867         
20868         this.inputEl().dom.checked = state;
20869         
20870         
20871         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20872         
20873         if(suppressEvent !== true){
20874             this.fireEvent('check', this, state);
20875         }
20876         
20877         this.validate();
20878     },
20879     
20880     getValue : function()
20881     {
20882         if(this.inputType == 'radio'){
20883             return this.getGroupValue();
20884         }
20885         
20886         return this.hiddenEl().dom.value;
20887         
20888     },
20889     
20890     getGroupValue : function()
20891     {
20892         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20893             return '';
20894         }
20895         
20896         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20897     },
20898     
20899     setValue : function(v,suppressEvent)
20900     {
20901         if(this.inputType == 'radio'){
20902             this.setGroupValue(v, suppressEvent);
20903             return;
20904         }
20905         
20906         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20907         
20908         this.validate();
20909     },
20910     
20911     setGroupValue : function(v, suppressEvent)
20912     {
20913         this.startValue = this.getValue();
20914         
20915         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20916             e.dom.checked = false;
20917             
20918             if(e.dom.value == v){
20919                 e.dom.checked = true;
20920             }
20921         });
20922         
20923         if(suppressEvent !== true){
20924             this.fireEvent('check', this, true);
20925         }
20926
20927         this.validate();
20928         
20929         return;
20930     },
20931     
20932     validate : function()
20933     {
20934         if(this.getVisibilityEl().hasClass('hidden')){
20935             return true;
20936         }
20937         
20938         if(
20939                 this.disabled || 
20940                 (this.inputType == 'radio' && this.validateRadio()) ||
20941                 (this.inputType == 'checkbox' && this.validateCheckbox())
20942         ){
20943             this.markValid();
20944             return true;
20945         }
20946         
20947         this.markInvalid();
20948         return false;
20949     },
20950     
20951     validateRadio : function()
20952     {
20953         if(this.getVisibilityEl().hasClass('hidden')){
20954             return true;
20955         }
20956         
20957         if(this.allowBlank){
20958             return true;
20959         }
20960         
20961         var valid = false;
20962         
20963         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20964             if(!e.dom.checked){
20965                 return;
20966             }
20967             
20968             valid = true;
20969             
20970             return false;
20971         });
20972         
20973         return valid;
20974     },
20975     
20976     validateCheckbox : function()
20977     {
20978         if(!this.groupId){
20979             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20980             //return (this.getValue() == this.inputValue) ? true : false;
20981         }
20982         
20983         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20984         
20985         if(!group){
20986             return false;
20987         }
20988         
20989         var r = false;
20990         
20991         for(var i in group){
20992             if(group[i].el.isVisible(true)){
20993                 r = false;
20994                 break;
20995             }
20996             
20997             r = true;
20998         }
20999         
21000         for(var i in group){
21001             if(r){
21002                 break;
21003             }
21004             
21005             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21006         }
21007         
21008         return r;
21009     },
21010     
21011     /**
21012      * Mark this field as valid
21013      */
21014     markValid : function()
21015     {
21016         var _this = this;
21017         
21018         this.fireEvent('valid', this);
21019         
21020         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21021         
21022         if(this.groupId){
21023             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21024         }
21025         
21026         if(label){
21027             label.markValid();
21028         }
21029
21030         if(this.inputType == 'radio'){
21031             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21032                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21033                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21034             });
21035             
21036             return;
21037         }
21038
21039         if(!this.groupId){
21040             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21041             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21042             return;
21043         }
21044         
21045         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21046         
21047         if(!group){
21048             return;
21049         }
21050         
21051         for(var i in group){
21052             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21053             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21054         }
21055     },
21056     
21057      /**
21058      * Mark this field as invalid
21059      * @param {String} msg The validation message
21060      */
21061     markInvalid : function(msg)
21062     {
21063         if(this.allowBlank){
21064             return;
21065         }
21066         
21067         var _this = this;
21068         
21069         this.fireEvent('invalid', this, msg);
21070         
21071         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21072         
21073         if(this.groupId){
21074             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21075         }
21076         
21077         if(label){
21078             label.markInvalid();
21079         }
21080             
21081         if(this.inputType == 'radio'){
21082             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21083                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21084                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21085             });
21086             
21087             return;
21088         }
21089         
21090         if(!this.groupId){
21091             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21092             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21093             return;
21094         }
21095         
21096         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21097         
21098         if(!group){
21099             return;
21100         }
21101         
21102         for(var i in group){
21103             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21104             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21105         }
21106         
21107     },
21108     
21109     clearInvalid : function()
21110     {
21111         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21112         
21113         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21114         
21115         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21116         
21117         if (label && label.iconEl) {
21118             label.iconEl.removeClass(label.validClass);
21119             label.iconEl.removeClass(label.invalidClass);
21120         }
21121     },
21122     
21123     disable : function()
21124     {
21125         if(this.inputType != 'radio'){
21126             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21127             return;
21128         }
21129         
21130         var _this = this;
21131         
21132         if(this.rendered){
21133             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21134                 _this.getActionEl().addClass(this.disabledClass);
21135                 e.dom.disabled = true;
21136             });
21137         }
21138         
21139         this.disabled = true;
21140         this.fireEvent("disable", this);
21141         return this;
21142     },
21143
21144     enable : function()
21145     {
21146         if(this.inputType != 'radio'){
21147             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21148             return;
21149         }
21150         
21151         var _this = this;
21152         
21153         if(this.rendered){
21154             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21155                 _this.getActionEl().removeClass(this.disabledClass);
21156                 e.dom.disabled = false;
21157             });
21158         }
21159         
21160         this.disabled = false;
21161         this.fireEvent("enable", this);
21162         return this;
21163     },
21164     
21165     setBoxLabel : function(v)
21166     {
21167         this.boxLabel = v;
21168         
21169         if(this.rendered){
21170             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21171         }
21172     }
21173
21174 });
21175
21176 Roo.apply(Roo.bootstrap.CheckBox, {
21177     
21178     groups: {},
21179     
21180      /**
21181     * register a CheckBox Group
21182     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21183     */
21184     register : function(checkbox)
21185     {
21186         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21187             this.groups[checkbox.groupId] = {};
21188         }
21189         
21190         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21191             return;
21192         }
21193         
21194         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21195         
21196     },
21197     /**
21198     * fetch a CheckBox Group based on the group ID
21199     * @param {string} the group ID
21200     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21201     */
21202     get: function(groupId) {
21203         if (typeof(this.groups[groupId]) == 'undefined') {
21204             return false;
21205         }
21206         
21207         return this.groups[groupId] ;
21208     }
21209     
21210     
21211 });
21212 /*
21213  * - LGPL
21214  *
21215  * RadioItem
21216  * 
21217  */
21218
21219 /**
21220  * @class Roo.bootstrap.Radio
21221  * @extends Roo.bootstrap.Component
21222  * Bootstrap Radio class
21223  * @cfg {String} boxLabel - the label associated
21224  * @cfg {String} value - the value of radio
21225  * 
21226  * @constructor
21227  * Create a new Radio
21228  * @param {Object} config The config object
21229  */
21230 Roo.bootstrap.Radio = function(config){
21231     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21232     
21233 };
21234
21235 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21236     
21237     boxLabel : '',
21238     
21239     value : '',
21240     
21241     getAutoCreate : function()
21242     {
21243         var cfg = {
21244             tag : 'div',
21245             cls : 'form-group radio',
21246             cn : [
21247                 {
21248                     tag : 'label',
21249                     cls : 'box-label',
21250                     html : this.boxLabel
21251                 }
21252             ]
21253         };
21254         
21255         return cfg;
21256     },
21257     
21258     initEvents : function() 
21259     {
21260         this.parent().register(this);
21261         
21262         this.el.on('click', this.onClick, this);
21263         
21264     },
21265     
21266     onClick : function(e)
21267     {
21268         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21269             this.setChecked(true);
21270         }
21271     },
21272     
21273     setChecked : function(state, suppressEvent)
21274     {
21275         this.parent().setValue(this.value, suppressEvent);
21276         
21277     },
21278     
21279     setBoxLabel : function(v)
21280     {
21281         this.boxLabel = v;
21282         
21283         if(this.rendered){
21284             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21285         }
21286     }
21287     
21288 });
21289  
21290
21291  /*
21292  * - LGPL
21293  *
21294  * Input
21295  * 
21296  */
21297
21298 /**
21299  * @class Roo.bootstrap.SecurePass
21300  * @extends Roo.bootstrap.Input
21301  * Bootstrap SecurePass class
21302  *
21303  * 
21304  * @constructor
21305  * Create a new SecurePass
21306  * @param {Object} config The config object
21307  */
21308  
21309 Roo.bootstrap.SecurePass = function (config) {
21310     // these go here, so the translation tool can replace them..
21311     this.errors = {
21312         PwdEmpty: "Please type a password, and then retype it to confirm.",
21313         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21314         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21315         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21316         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21317         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21318         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21319         TooWeak: "Your password is Too Weak."
21320     },
21321     this.meterLabel = "Password strength:";
21322     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21323     this.meterClass = [
21324         "roo-password-meter-tooweak", 
21325         "roo-password-meter-weak", 
21326         "roo-password-meter-medium", 
21327         "roo-password-meter-strong", 
21328         "roo-password-meter-grey"
21329     ];
21330     
21331     this.errors = {};
21332     
21333     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21334 }
21335
21336 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21337     /**
21338      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21339      * {
21340      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21341      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21342      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21343      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21344      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21345      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21346      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21347      * })
21348      */
21349     // private
21350     
21351     meterWidth: 300,
21352     errorMsg :'',    
21353     errors: false,
21354     imageRoot: '/',
21355     /**
21356      * @cfg {String/Object} Label for the strength meter (defaults to
21357      * 'Password strength:')
21358      */
21359     // private
21360     meterLabel: '',
21361     /**
21362      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21363      * ['Weak', 'Medium', 'Strong'])
21364      */
21365     // private    
21366     pwdStrengths: false,    
21367     // private
21368     strength: 0,
21369     // private
21370     _lastPwd: null,
21371     // private
21372     kCapitalLetter: 0,
21373     kSmallLetter: 1,
21374     kDigit: 2,
21375     kPunctuation: 3,
21376     
21377     insecure: false,
21378     // private
21379     initEvents: function ()
21380     {
21381         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21382
21383         if (this.el.is('input[type=password]') && Roo.isSafari) {
21384             this.el.on('keydown', this.SafariOnKeyDown, this);
21385         }
21386
21387         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21388     },
21389     // private
21390     onRender: function (ct, position)
21391     {
21392         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21393         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21394         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21395
21396         this.trigger.createChild({
21397                    cn: [
21398                     {
21399                     //id: 'PwdMeter',
21400                     tag: 'div',
21401                     cls: 'roo-password-meter-grey col-xs-12',
21402                     style: {
21403                         //width: 0,
21404                         //width: this.meterWidth + 'px'                                                
21405                         }
21406                     },
21407                     {                            
21408                          cls: 'roo-password-meter-text'                          
21409                     }
21410                 ]            
21411         });
21412
21413          
21414         if (this.hideTrigger) {
21415             this.trigger.setDisplayed(false);
21416         }
21417         this.setSize(this.width || '', this.height || '');
21418     },
21419     // private
21420     onDestroy: function ()
21421     {
21422         if (this.trigger) {
21423             this.trigger.removeAllListeners();
21424             this.trigger.remove();
21425         }
21426         if (this.wrap) {
21427             this.wrap.remove();
21428         }
21429         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21430     },
21431     // private
21432     checkStrength: function ()
21433     {
21434         var pwd = this.inputEl().getValue();
21435         if (pwd == this._lastPwd) {
21436             return;
21437         }
21438
21439         var strength;
21440         if (this.ClientSideStrongPassword(pwd)) {
21441             strength = 3;
21442         } else if (this.ClientSideMediumPassword(pwd)) {
21443             strength = 2;
21444         } else if (this.ClientSideWeakPassword(pwd)) {
21445             strength = 1;
21446         } else {
21447             strength = 0;
21448         }
21449         
21450         Roo.log('strength1: ' + strength);
21451         
21452         //var pm = this.trigger.child('div/div/div').dom;
21453         var pm = this.trigger.child('div/div');
21454         pm.removeClass(this.meterClass);
21455         pm.addClass(this.meterClass[strength]);
21456                 
21457         
21458         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21459                 
21460         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21461         
21462         this._lastPwd = pwd;
21463     },
21464     reset: function ()
21465     {
21466         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21467         
21468         this._lastPwd = '';
21469         
21470         var pm = this.trigger.child('div/div');
21471         pm.removeClass(this.meterClass);
21472         pm.addClass('roo-password-meter-grey');        
21473         
21474         
21475         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21476         
21477         pt.innerHTML = '';
21478         this.inputEl().dom.type='password';
21479     },
21480     // private
21481     validateValue: function (value)
21482     {
21483         
21484         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21485             return false;
21486         }
21487         if (value.length == 0) {
21488             if (this.allowBlank) {
21489                 this.clearInvalid();
21490                 return true;
21491             }
21492
21493             this.markInvalid(this.errors.PwdEmpty);
21494             this.errorMsg = this.errors.PwdEmpty;
21495             return false;
21496         }
21497         
21498         if(this.insecure){
21499             return true;
21500         }
21501         
21502         if ('[\x21-\x7e]*'.match(value)) {
21503             this.markInvalid(this.errors.PwdBadChar);
21504             this.errorMsg = this.errors.PwdBadChar;
21505             return false;
21506         }
21507         if (value.length < 6) {
21508             this.markInvalid(this.errors.PwdShort);
21509             this.errorMsg = this.errors.PwdShort;
21510             return false;
21511         }
21512         if (value.length > 16) {
21513             this.markInvalid(this.errors.PwdLong);
21514             this.errorMsg = this.errors.PwdLong;
21515             return false;
21516         }
21517         var strength;
21518         if (this.ClientSideStrongPassword(value)) {
21519             strength = 3;
21520         } else if (this.ClientSideMediumPassword(value)) {
21521             strength = 2;
21522         } else if (this.ClientSideWeakPassword(value)) {
21523             strength = 1;
21524         } else {
21525             strength = 0;
21526         }
21527
21528         
21529         if (strength < 2) {
21530             //this.markInvalid(this.errors.TooWeak);
21531             this.errorMsg = this.errors.TooWeak;
21532             //return false;
21533         }
21534         
21535         
21536         console.log('strength2: ' + strength);
21537         
21538         //var pm = this.trigger.child('div/div/div').dom;
21539         
21540         var pm = this.trigger.child('div/div');
21541         pm.removeClass(this.meterClass);
21542         pm.addClass(this.meterClass[strength]);
21543                 
21544         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21545                 
21546         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21547         
21548         this.errorMsg = ''; 
21549         return true;
21550     },
21551     // private
21552     CharacterSetChecks: function (type)
21553     {
21554         this.type = type;
21555         this.fResult = false;
21556     },
21557     // private
21558     isctype: function (character, type)
21559     {
21560         switch (type) {  
21561             case this.kCapitalLetter:
21562                 if (character >= 'A' && character <= 'Z') {
21563                     return true;
21564                 }
21565                 break;
21566             
21567             case this.kSmallLetter:
21568                 if (character >= 'a' && character <= 'z') {
21569                     return true;
21570                 }
21571                 break;
21572             
21573             case this.kDigit:
21574                 if (character >= '0' && character <= '9') {
21575                     return true;
21576                 }
21577                 break;
21578             
21579             case this.kPunctuation:
21580                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21581                     return true;
21582                 }
21583                 break;
21584             
21585             default:
21586                 return false;
21587         }
21588
21589     },
21590     // private
21591     IsLongEnough: function (pwd, size)
21592     {
21593         return !(pwd == null || isNaN(size) || pwd.length < size);
21594     },
21595     // private
21596     SpansEnoughCharacterSets: function (word, nb)
21597     {
21598         if (!this.IsLongEnough(word, nb))
21599         {
21600             return false;
21601         }
21602
21603         var characterSetChecks = new Array(
21604             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21605             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21606         );
21607         
21608         for (var index = 0; index < word.length; ++index) {
21609             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21610                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21611                     characterSetChecks[nCharSet].fResult = true;
21612                     break;
21613                 }
21614             }
21615         }
21616
21617         var nCharSets = 0;
21618         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21619             if (characterSetChecks[nCharSet].fResult) {
21620                 ++nCharSets;
21621             }
21622         }
21623
21624         if (nCharSets < nb) {
21625             return false;
21626         }
21627         return true;
21628     },
21629     // private
21630     ClientSideStrongPassword: function (pwd)
21631     {
21632         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21633     },
21634     // private
21635     ClientSideMediumPassword: function (pwd)
21636     {
21637         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21638     },
21639     // private
21640     ClientSideWeakPassword: function (pwd)
21641     {
21642         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21643     }
21644           
21645 })//<script type="text/javascript">
21646
21647 /*
21648  * Based  Ext JS Library 1.1.1
21649  * Copyright(c) 2006-2007, Ext JS, LLC.
21650  * LGPL
21651  *
21652  */
21653  
21654 /**
21655  * @class Roo.HtmlEditorCore
21656  * @extends Roo.Component
21657  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21658  *
21659  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21660  */
21661
21662 Roo.HtmlEditorCore = function(config){
21663     
21664     
21665     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21666     
21667     
21668     this.addEvents({
21669         /**
21670          * @event initialize
21671          * Fires when the editor is fully initialized (including the iframe)
21672          * @param {Roo.HtmlEditorCore} this
21673          */
21674         initialize: true,
21675         /**
21676          * @event activate
21677          * Fires when the editor is first receives the focus. Any insertion must wait
21678          * until after this event.
21679          * @param {Roo.HtmlEditorCore} this
21680          */
21681         activate: true,
21682          /**
21683          * @event beforesync
21684          * Fires before the textarea is updated with content from the editor iframe. Return false
21685          * to cancel the sync.
21686          * @param {Roo.HtmlEditorCore} this
21687          * @param {String} html
21688          */
21689         beforesync: true,
21690          /**
21691          * @event beforepush
21692          * Fires before the iframe editor is updated with content from the textarea. Return false
21693          * to cancel the push.
21694          * @param {Roo.HtmlEditorCore} this
21695          * @param {String} html
21696          */
21697         beforepush: true,
21698          /**
21699          * @event sync
21700          * Fires when the textarea is updated with content from the editor iframe.
21701          * @param {Roo.HtmlEditorCore} this
21702          * @param {String} html
21703          */
21704         sync: true,
21705          /**
21706          * @event push
21707          * Fires when the iframe editor is updated with content from the textarea.
21708          * @param {Roo.HtmlEditorCore} this
21709          * @param {String} html
21710          */
21711         push: true,
21712         
21713         /**
21714          * @event editorevent
21715          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21716          * @param {Roo.HtmlEditorCore} this
21717          */
21718         editorevent: true
21719         
21720     });
21721     
21722     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21723     
21724     // defaults : white / black...
21725     this.applyBlacklists();
21726     
21727     
21728     
21729 };
21730
21731
21732 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21733
21734
21735      /**
21736      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21737      */
21738     
21739     owner : false,
21740     
21741      /**
21742      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21743      *                        Roo.resizable.
21744      */
21745     resizable : false,
21746      /**
21747      * @cfg {Number} height (in pixels)
21748      */   
21749     height: 300,
21750    /**
21751      * @cfg {Number} width (in pixels)
21752      */   
21753     width: 500,
21754     
21755     /**
21756      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21757      * 
21758      */
21759     stylesheets: false,
21760     
21761     // id of frame..
21762     frameId: false,
21763     
21764     // private properties
21765     validationEvent : false,
21766     deferHeight: true,
21767     initialized : false,
21768     activated : false,
21769     sourceEditMode : false,
21770     onFocus : Roo.emptyFn,
21771     iframePad:3,
21772     hideMode:'offsets',
21773     
21774     clearUp: true,
21775     
21776     // blacklist + whitelisted elements..
21777     black: false,
21778     white: false,
21779      
21780     bodyCls : '',
21781
21782     /**
21783      * Protected method that will not generally be called directly. It
21784      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21785      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21786      */
21787     getDocMarkup : function(){
21788         // body styles..
21789         var st = '';
21790         
21791         // inherit styels from page...?? 
21792         if (this.stylesheets === false) {
21793             
21794             Roo.get(document.head).select('style').each(function(node) {
21795                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21796             });
21797             
21798             Roo.get(document.head).select('link').each(function(node) { 
21799                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21800             });
21801             
21802         } else if (!this.stylesheets.length) {
21803                 // simple..
21804                 st = '<style type="text/css">' +
21805                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21806                    '</style>';
21807         } else { 
21808             st = '<style type="text/css">' +
21809                     this.stylesheets +
21810                 '</style>';
21811         }
21812         
21813         st +=  '<style type="text/css">' +
21814             'IMG { cursor: pointer } ' +
21815         '</style>';
21816
21817         var cls = 'roo-htmleditor-body';
21818         
21819         if(this.bodyCls.length){
21820             cls += ' ' + this.bodyCls;
21821         }
21822         
21823         return '<html><head>' + st  +
21824             //<style type="text/css">' +
21825             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21826             //'</style>' +
21827             ' </head><body class="' +  cls + '"></body></html>';
21828     },
21829
21830     // private
21831     onRender : function(ct, position)
21832     {
21833         var _t = this;
21834         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21835         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21836         
21837         
21838         this.el.dom.style.border = '0 none';
21839         this.el.dom.setAttribute('tabIndex', -1);
21840         this.el.addClass('x-hidden hide');
21841         
21842         
21843         
21844         if(Roo.isIE){ // fix IE 1px bogus margin
21845             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21846         }
21847        
21848         
21849         this.frameId = Roo.id();
21850         
21851          
21852         
21853         var iframe = this.owner.wrap.createChild({
21854             tag: 'iframe',
21855             cls: 'form-control', // bootstrap..
21856             id: this.frameId,
21857             name: this.frameId,
21858             frameBorder : 'no',
21859             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21860         }, this.el
21861         );
21862         
21863         
21864         this.iframe = iframe.dom;
21865
21866          this.assignDocWin();
21867         
21868         this.doc.designMode = 'on';
21869        
21870         this.doc.open();
21871         this.doc.write(this.getDocMarkup());
21872         this.doc.close();
21873
21874         
21875         var task = { // must defer to wait for browser to be ready
21876             run : function(){
21877                 //console.log("run task?" + this.doc.readyState);
21878                 this.assignDocWin();
21879                 if(this.doc.body || this.doc.readyState == 'complete'){
21880                     try {
21881                         this.doc.designMode="on";
21882                     } catch (e) {
21883                         return;
21884                     }
21885                     Roo.TaskMgr.stop(task);
21886                     this.initEditor.defer(10, this);
21887                 }
21888             },
21889             interval : 10,
21890             duration: 10000,
21891             scope: this
21892         };
21893         Roo.TaskMgr.start(task);
21894
21895     },
21896
21897     // private
21898     onResize : function(w, h)
21899     {
21900          Roo.log('resize: ' +w + ',' + h );
21901         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21902         if(!this.iframe){
21903             return;
21904         }
21905         if(typeof w == 'number'){
21906             
21907             this.iframe.style.width = w + 'px';
21908         }
21909         if(typeof h == 'number'){
21910             
21911             this.iframe.style.height = h + 'px';
21912             if(this.doc){
21913                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21914             }
21915         }
21916         
21917     },
21918
21919     /**
21920      * Toggles the editor between standard and source edit mode.
21921      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21922      */
21923     toggleSourceEdit : function(sourceEditMode){
21924         
21925         this.sourceEditMode = sourceEditMode === true;
21926         
21927         if(this.sourceEditMode){
21928  
21929             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21930             
21931         }else{
21932             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21933             //this.iframe.className = '';
21934             this.deferFocus();
21935         }
21936         //this.setSize(this.owner.wrap.getSize());
21937         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21938     },
21939
21940     
21941   
21942
21943     /**
21944      * Protected method that will not generally be called directly. If you need/want
21945      * custom HTML cleanup, this is the method you should override.
21946      * @param {String} html The HTML to be cleaned
21947      * return {String} The cleaned HTML
21948      */
21949     cleanHtml : function(html){
21950         html = String(html);
21951         if(html.length > 5){
21952             if(Roo.isSafari){ // strip safari nonsense
21953                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21954             }
21955         }
21956         if(html == '&nbsp;'){
21957             html = '';
21958         }
21959         return html;
21960     },
21961
21962     /**
21963      * HTML Editor -> Textarea
21964      * Protected method that will not generally be called directly. Syncs the contents
21965      * of the editor iframe with the textarea.
21966      */
21967     syncValue : function(){
21968         if(this.initialized){
21969             var bd = (this.doc.body || this.doc.documentElement);
21970             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21971             var html = bd.innerHTML;
21972             if(Roo.isSafari){
21973                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21974                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21975                 if(m && m[1]){
21976                     html = '<div style="'+m[0]+'">' + html + '</div>';
21977                 }
21978             }
21979             html = this.cleanHtml(html);
21980             // fix up the special chars.. normaly like back quotes in word...
21981             // however we do not want to do this with chinese..
21982             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21983                 var cc = b.charCodeAt();
21984                 if (
21985                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21986                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21987                     (cc >= 0xf900 && cc < 0xfb00 )
21988                 ) {
21989                         return b;
21990                 }
21991                 return "&#"+cc+";" 
21992             });
21993             if(this.owner.fireEvent('beforesync', this, html) !== false){
21994                 this.el.dom.value = html;
21995                 this.owner.fireEvent('sync', this, html);
21996             }
21997         }
21998     },
21999
22000     /**
22001      * Protected method that will not generally be called directly. Pushes the value of the textarea
22002      * into the iframe editor.
22003      */
22004     pushValue : function(){
22005         if(this.initialized){
22006             var v = this.el.dom.value.trim();
22007             
22008 //            if(v.length < 1){
22009 //                v = '&#160;';
22010 //            }
22011             
22012             if(this.owner.fireEvent('beforepush', this, v) !== false){
22013                 var d = (this.doc.body || this.doc.documentElement);
22014                 d.innerHTML = v;
22015                 this.cleanUpPaste();
22016                 this.el.dom.value = d.innerHTML;
22017                 this.owner.fireEvent('push', this, v);
22018             }
22019         }
22020     },
22021
22022     // private
22023     deferFocus : function(){
22024         this.focus.defer(10, this);
22025     },
22026
22027     // doc'ed in Field
22028     focus : function(){
22029         if(this.win && !this.sourceEditMode){
22030             this.win.focus();
22031         }else{
22032             this.el.focus();
22033         }
22034     },
22035     
22036     assignDocWin: function()
22037     {
22038         var iframe = this.iframe;
22039         
22040          if(Roo.isIE){
22041             this.doc = iframe.contentWindow.document;
22042             this.win = iframe.contentWindow;
22043         } else {
22044 //            if (!Roo.get(this.frameId)) {
22045 //                return;
22046 //            }
22047 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22048 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22049             
22050             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22051                 return;
22052             }
22053             
22054             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22055             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22056         }
22057     },
22058     
22059     // private
22060     initEditor : function(){
22061         //console.log("INIT EDITOR");
22062         this.assignDocWin();
22063         
22064         
22065         
22066         this.doc.designMode="on";
22067         this.doc.open();
22068         this.doc.write(this.getDocMarkup());
22069         this.doc.close();
22070         
22071         var dbody = (this.doc.body || this.doc.documentElement);
22072         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22073         // this copies styles from the containing element into thsi one..
22074         // not sure why we need all of this..
22075         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22076         
22077         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22078         //ss['background-attachment'] = 'fixed'; // w3c
22079         dbody.bgProperties = 'fixed'; // ie
22080         //Roo.DomHelper.applyStyles(dbody, ss);
22081         Roo.EventManager.on(this.doc, {
22082             //'mousedown': this.onEditorEvent,
22083             'mouseup': this.onEditorEvent,
22084             'dblclick': this.onEditorEvent,
22085             'click': this.onEditorEvent,
22086             'keyup': this.onEditorEvent,
22087             buffer:100,
22088             scope: this
22089         });
22090         if(Roo.isGecko){
22091             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22092         }
22093         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22094             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22095         }
22096         this.initialized = true;
22097
22098         this.owner.fireEvent('initialize', this);
22099         this.pushValue();
22100     },
22101
22102     // private
22103     onDestroy : function(){
22104         
22105         
22106         
22107         if(this.rendered){
22108             
22109             //for (var i =0; i < this.toolbars.length;i++) {
22110             //    // fixme - ask toolbars for heights?
22111             //    this.toolbars[i].onDestroy();
22112            // }
22113             
22114             //this.wrap.dom.innerHTML = '';
22115             //this.wrap.remove();
22116         }
22117     },
22118
22119     // private
22120     onFirstFocus : function(){
22121         
22122         this.assignDocWin();
22123         
22124         
22125         this.activated = true;
22126          
22127     
22128         if(Roo.isGecko){ // prevent silly gecko errors
22129             this.win.focus();
22130             var s = this.win.getSelection();
22131             if(!s.focusNode || s.focusNode.nodeType != 3){
22132                 var r = s.getRangeAt(0);
22133                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22134                 r.collapse(true);
22135                 this.deferFocus();
22136             }
22137             try{
22138                 this.execCmd('useCSS', true);
22139                 this.execCmd('styleWithCSS', false);
22140             }catch(e){}
22141         }
22142         this.owner.fireEvent('activate', this);
22143     },
22144
22145     // private
22146     adjustFont: function(btn){
22147         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22148         //if(Roo.isSafari){ // safari
22149         //    adjust *= 2;
22150        // }
22151         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22152         if(Roo.isSafari){ // safari
22153             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22154             v =  (v < 10) ? 10 : v;
22155             v =  (v > 48) ? 48 : v;
22156             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22157             
22158         }
22159         
22160         
22161         v = Math.max(1, v+adjust);
22162         
22163         this.execCmd('FontSize', v  );
22164     },
22165
22166     onEditorEvent : function(e)
22167     {
22168         this.owner.fireEvent('editorevent', this, e);
22169       //  this.updateToolbar();
22170         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22171     },
22172
22173     insertTag : function(tg)
22174     {
22175         // could be a bit smarter... -> wrap the current selected tRoo..
22176         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22177             
22178             range = this.createRange(this.getSelection());
22179             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22180             wrappingNode.appendChild(range.extractContents());
22181             range.insertNode(wrappingNode);
22182
22183             return;
22184             
22185             
22186             
22187         }
22188         this.execCmd("formatblock",   tg);
22189         
22190     },
22191     
22192     insertText : function(txt)
22193     {
22194         
22195         
22196         var range = this.createRange();
22197         range.deleteContents();
22198                //alert(Sender.getAttribute('label'));
22199                
22200         range.insertNode(this.doc.createTextNode(txt));
22201     } ,
22202     
22203      
22204
22205     /**
22206      * Executes a Midas editor command on the editor document and performs necessary focus and
22207      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22208      * @param {String} cmd The Midas command
22209      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22210      */
22211     relayCmd : function(cmd, value){
22212         this.win.focus();
22213         this.execCmd(cmd, value);
22214         this.owner.fireEvent('editorevent', this);
22215         //this.updateToolbar();
22216         this.owner.deferFocus();
22217     },
22218
22219     /**
22220      * Executes a Midas editor command directly on the editor document.
22221      * For visual commands, you should use {@link #relayCmd} instead.
22222      * <b>This should only be called after the editor is initialized.</b>
22223      * @param {String} cmd The Midas command
22224      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22225      */
22226     execCmd : function(cmd, value){
22227         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22228         this.syncValue();
22229     },
22230  
22231  
22232    
22233     /**
22234      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22235      * to insert tRoo.
22236      * @param {String} text | dom node.. 
22237      */
22238     insertAtCursor : function(text)
22239     {
22240         
22241         if(!this.activated){
22242             return;
22243         }
22244         /*
22245         if(Roo.isIE){
22246             this.win.focus();
22247             var r = this.doc.selection.createRange();
22248             if(r){
22249                 r.collapse(true);
22250                 r.pasteHTML(text);
22251                 this.syncValue();
22252                 this.deferFocus();
22253             
22254             }
22255             return;
22256         }
22257         */
22258         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22259             this.win.focus();
22260             
22261             
22262             // from jquery ui (MIT licenced)
22263             var range, node;
22264             var win = this.win;
22265             
22266             if (win.getSelection && win.getSelection().getRangeAt) {
22267                 range = win.getSelection().getRangeAt(0);
22268                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22269                 range.insertNode(node);
22270             } else if (win.document.selection && win.document.selection.createRange) {
22271                 // no firefox support
22272                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22273                 win.document.selection.createRange().pasteHTML(txt);
22274             } else {
22275                 // no firefox support
22276                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22277                 this.execCmd('InsertHTML', txt);
22278             } 
22279             
22280             this.syncValue();
22281             
22282             this.deferFocus();
22283         }
22284     },
22285  // private
22286     mozKeyPress : function(e){
22287         if(e.ctrlKey){
22288             var c = e.getCharCode(), cmd;
22289           
22290             if(c > 0){
22291                 c = String.fromCharCode(c).toLowerCase();
22292                 switch(c){
22293                     case 'b':
22294                         cmd = 'bold';
22295                         break;
22296                     case 'i':
22297                         cmd = 'italic';
22298                         break;
22299                     
22300                     case 'u':
22301                         cmd = 'underline';
22302                         break;
22303                     
22304                     case 'v':
22305                         this.cleanUpPaste.defer(100, this);
22306                         return;
22307                         
22308                 }
22309                 if(cmd){
22310                     this.win.focus();
22311                     this.execCmd(cmd);
22312                     this.deferFocus();
22313                     e.preventDefault();
22314                 }
22315                 
22316             }
22317         }
22318     },
22319
22320     // private
22321     fixKeys : function(){ // load time branching for fastest keydown performance
22322         if(Roo.isIE){
22323             return function(e){
22324                 var k = e.getKey(), r;
22325                 if(k == e.TAB){
22326                     e.stopEvent();
22327                     r = this.doc.selection.createRange();
22328                     if(r){
22329                         r.collapse(true);
22330                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22331                         this.deferFocus();
22332                     }
22333                     return;
22334                 }
22335                 
22336                 if(k == e.ENTER){
22337                     r = this.doc.selection.createRange();
22338                     if(r){
22339                         var target = r.parentElement();
22340                         if(!target || target.tagName.toLowerCase() != 'li'){
22341                             e.stopEvent();
22342                             r.pasteHTML('<br />');
22343                             r.collapse(false);
22344                             r.select();
22345                         }
22346                     }
22347                 }
22348                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22349                     this.cleanUpPaste.defer(100, this);
22350                     return;
22351                 }
22352                 
22353                 
22354             };
22355         }else if(Roo.isOpera){
22356             return function(e){
22357                 var k = e.getKey();
22358                 if(k == e.TAB){
22359                     e.stopEvent();
22360                     this.win.focus();
22361                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22362                     this.deferFocus();
22363                 }
22364                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22365                     this.cleanUpPaste.defer(100, this);
22366                     return;
22367                 }
22368                 
22369             };
22370         }else if(Roo.isSafari){
22371             return function(e){
22372                 var k = e.getKey();
22373                 
22374                 if(k == e.TAB){
22375                     e.stopEvent();
22376                     this.execCmd('InsertText','\t');
22377                     this.deferFocus();
22378                     return;
22379                 }
22380                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22381                     this.cleanUpPaste.defer(100, this);
22382                     return;
22383                 }
22384                 
22385              };
22386         }
22387     }(),
22388     
22389     getAllAncestors: function()
22390     {
22391         var p = this.getSelectedNode();
22392         var a = [];
22393         if (!p) {
22394             a.push(p); // push blank onto stack..
22395             p = this.getParentElement();
22396         }
22397         
22398         
22399         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22400             a.push(p);
22401             p = p.parentNode;
22402         }
22403         a.push(this.doc.body);
22404         return a;
22405     },
22406     lastSel : false,
22407     lastSelNode : false,
22408     
22409     
22410     getSelection : function() 
22411     {
22412         this.assignDocWin();
22413         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22414     },
22415     
22416     getSelectedNode: function() 
22417     {
22418         // this may only work on Gecko!!!
22419         
22420         // should we cache this!!!!
22421         
22422         
22423         
22424          
22425         var range = this.createRange(this.getSelection()).cloneRange();
22426         
22427         if (Roo.isIE) {
22428             var parent = range.parentElement();
22429             while (true) {
22430                 var testRange = range.duplicate();
22431                 testRange.moveToElementText(parent);
22432                 if (testRange.inRange(range)) {
22433                     break;
22434                 }
22435                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22436                     break;
22437                 }
22438                 parent = parent.parentElement;
22439             }
22440             return parent;
22441         }
22442         
22443         // is ancestor a text element.
22444         var ac =  range.commonAncestorContainer;
22445         if (ac.nodeType == 3) {
22446             ac = ac.parentNode;
22447         }
22448         
22449         var ar = ac.childNodes;
22450          
22451         var nodes = [];
22452         var other_nodes = [];
22453         var has_other_nodes = false;
22454         for (var i=0;i<ar.length;i++) {
22455             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22456                 continue;
22457             }
22458             // fullly contained node.
22459             
22460             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22461                 nodes.push(ar[i]);
22462                 continue;
22463             }
22464             
22465             // probably selected..
22466             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22467                 other_nodes.push(ar[i]);
22468                 continue;
22469             }
22470             // outer..
22471             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22472                 continue;
22473             }
22474             
22475             
22476             has_other_nodes = true;
22477         }
22478         if (!nodes.length && other_nodes.length) {
22479             nodes= other_nodes;
22480         }
22481         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22482             return false;
22483         }
22484         
22485         return nodes[0];
22486     },
22487     createRange: function(sel)
22488     {
22489         // this has strange effects when using with 
22490         // top toolbar - not sure if it's a great idea.
22491         //this.editor.contentWindow.focus();
22492         if (typeof sel != "undefined") {
22493             try {
22494                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22495             } catch(e) {
22496                 return this.doc.createRange();
22497             }
22498         } else {
22499             return this.doc.createRange();
22500         }
22501     },
22502     getParentElement: function()
22503     {
22504         
22505         this.assignDocWin();
22506         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22507         
22508         var range = this.createRange(sel);
22509          
22510         try {
22511             var p = range.commonAncestorContainer;
22512             while (p.nodeType == 3) { // text node
22513                 p = p.parentNode;
22514             }
22515             return p;
22516         } catch (e) {
22517             return null;
22518         }
22519     
22520     },
22521     /***
22522      *
22523      * Range intersection.. the hard stuff...
22524      *  '-1' = before
22525      *  '0' = hits..
22526      *  '1' = after.
22527      *         [ -- selected range --- ]
22528      *   [fail]                        [fail]
22529      *
22530      *    basically..
22531      *      if end is before start or  hits it. fail.
22532      *      if start is after end or hits it fail.
22533      *
22534      *   if either hits (but other is outside. - then it's not 
22535      *   
22536      *    
22537      **/
22538     
22539     
22540     // @see http://www.thismuchiknow.co.uk/?p=64.
22541     rangeIntersectsNode : function(range, node)
22542     {
22543         var nodeRange = node.ownerDocument.createRange();
22544         try {
22545             nodeRange.selectNode(node);
22546         } catch (e) {
22547             nodeRange.selectNodeContents(node);
22548         }
22549     
22550         var rangeStartRange = range.cloneRange();
22551         rangeStartRange.collapse(true);
22552     
22553         var rangeEndRange = range.cloneRange();
22554         rangeEndRange.collapse(false);
22555     
22556         var nodeStartRange = nodeRange.cloneRange();
22557         nodeStartRange.collapse(true);
22558     
22559         var nodeEndRange = nodeRange.cloneRange();
22560         nodeEndRange.collapse(false);
22561     
22562         return rangeStartRange.compareBoundaryPoints(
22563                  Range.START_TO_START, nodeEndRange) == -1 &&
22564                rangeEndRange.compareBoundaryPoints(
22565                  Range.START_TO_START, nodeStartRange) == 1;
22566         
22567          
22568     },
22569     rangeCompareNode : function(range, node)
22570     {
22571         var nodeRange = node.ownerDocument.createRange();
22572         try {
22573             nodeRange.selectNode(node);
22574         } catch (e) {
22575             nodeRange.selectNodeContents(node);
22576         }
22577         
22578         
22579         range.collapse(true);
22580     
22581         nodeRange.collapse(true);
22582      
22583         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22584         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22585          
22586         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22587         
22588         var nodeIsBefore   =  ss == 1;
22589         var nodeIsAfter    = ee == -1;
22590         
22591         if (nodeIsBefore && nodeIsAfter) {
22592             return 0; // outer
22593         }
22594         if (!nodeIsBefore && nodeIsAfter) {
22595             return 1; //right trailed.
22596         }
22597         
22598         if (nodeIsBefore && !nodeIsAfter) {
22599             return 2;  // left trailed.
22600         }
22601         // fully contined.
22602         return 3;
22603     },
22604
22605     // private? - in a new class?
22606     cleanUpPaste :  function()
22607     {
22608         // cleans up the whole document..
22609         Roo.log('cleanuppaste');
22610         
22611         this.cleanUpChildren(this.doc.body);
22612         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22613         if (clean != this.doc.body.innerHTML) {
22614             this.doc.body.innerHTML = clean;
22615         }
22616         
22617     },
22618     
22619     cleanWordChars : function(input) {// change the chars to hex code
22620         var he = Roo.HtmlEditorCore;
22621         
22622         var output = input;
22623         Roo.each(he.swapCodes, function(sw) { 
22624             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22625             
22626             output = output.replace(swapper, sw[1]);
22627         });
22628         
22629         return output;
22630     },
22631     
22632     
22633     cleanUpChildren : function (n)
22634     {
22635         if (!n.childNodes.length) {
22636             return;
22637         }
22638         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22639            this.cleanUpChild(n.childNodes[i]);
22640         }
22641     },
22642     
22643     
22644         
22645     
22646     cleanUpChild : function (node)
22647     {
22648         var ed = this;
22649         //console.log(node);
22650         if (node.nodeName == "#text") {
22651             // clean up silly Windows -- stuff?
22652             return; 
22653         }
22654         if (node.nodeName == "#comment") {
22655             node.parentNode.removeChild(node);
22656             // clean up silly Windows -- stuff?
22657             return; 
22658         }
22659         var lcname = node.tagName.toLowerCase();
22660         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22661         // whitelist of tags..
22662         
22663         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22664             // remove node.
22665             node.parentNode.removeChild(node);
22666             return;
22667             
22668         }
22669         
22670         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22671         
22672         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22673         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22674         
22675         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22676         //    remove_keep_children = true;
22677         //}
22678         
22679         if (remove_keep_children) {
22680             this.cleanUpChildren(node);
22681             // inserts everything just before this node...
22682             while (node.childNodes.length) {
22683                 var cn = node.childNodes[0];
22684                 node.removeChild(cn);
22685                 node.parentNode.insertBefore(cn, node);
22686             }
22687             node.parentNode.removeChild(node);
22688             return;
22689         }
22690         
22691         if (!node.attributes || !node.attributes.length) {
22692             this.cleanUpChildren(node);
22693             return;
22694         }
22695         
22696         function cleanAttr(n,v)
22697         {
22698             
22699             if (v.match(/^\./) || v.match(/^\//)) {
22700                 return;
22701             }
22702             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22703                 return;
22704             }
22705             if (v.match(/^#/)) {
22706                 return;
22707             }
22708 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22709             node.removeAttribute(n);
22710             
22711         }
22712         
22713         var cwhite = this.cwhite;
22714         var cblack = this.cblack;
22715             
22716         function cleanStyle(n,v)
22717         {
22718             if (v.match(/expression/)) { //XSS?? should we even bother..
22719                 node.removeAttribute(n);
22720                 return;
22721             }
22722             
22723             var parts = v.split(/;/);
22724             var clean = [];
22725             
22726             Roo.each(parts, function(p) {
22727                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22728                 if (!p.length) {
22729                     return true;
22730                 }
22731                 var l = p.split(':').shift().replace(/\s+/g,'');
22732                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22733                 
22734                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22735 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22736                     //node.removeAttribute(n);
22737                     return true;
22738                 }
22739                 //Roo.log()
22740                 // only allow 'c whitelisted system attributes'
22741                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22742 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22743                     //node.removeAttribute(n);
22744                     return true;
22745                 }
22746                 
22747                 
22748                  
22749                 
22750                 clean.push(p);
22751                 return true;
22752             });
22753             if (clean.length) { 
22754                 node.setAttribute(n, clean.join(';'));
22755             } else {
22756                 node.removeAttribute(n);
22757             }
22758             
22759         }
22760         
22761         
22762         for (var i = node.attributes.length-1; i > -1 ; i--) {
22763             var a = node.attributes[i];
22764             //console.log(a);
22765             
22766             if (a.name.toLowerCase().substr(0,2)=='on')  {
22767                 node.removeAttribute(a.name);
22768                 continue;
22769             }
22770             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22771                 node.removeAttribute(a.name);
22772                 continue;
22773             }
22774             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22775                 cleanAttr(a.name,a.value); // fixme..
22776                 continue;
22777             }
22778             if (a.name == 'style') {
22779                 cleanStyle(a.name,a.value);
22780                 continue;
22781             }
22782             /// clean up MS crap..
22783             // tecnically this should be a list of valid class'es..
22784             
22785             
22786             if (a.name == 'class') {
22787                 if (a.value.match(/^Mso/)) {
22788                     node.className = '';
22789                 }
22790                 
22791                 if (a.value.match(/^body$/)) {
22792                     node.className = '';
22793                 }
22794                 continue;
22795             }
22796             
22797             // style cleanup!?
22798             // class cleanup?
22799             
22800         }
22801         
22802         
22803         this.cleanUpChildren(node);
22804         
22805         
22806     },
22807     
22808     /**
22809      * Clean up MS wordisms...
22810      */
22811     cleanWord : function(node)
22812     {
22813         
22814         
22815         if (!node) {
22816             this.cleanWord(this.doc.body);
22817             return;
22818         }
22819         if (node.nodeName == "#text") {
22820             // clean up silly Windows -- stuff?
22821             return; 
22822         }
22823         if (node.nodeName == "#comment") {
22824             node.parentNode.removeChild(node);
22825             // clean up silly Windows -- stuff?
22826             return; 
22827         }
22828         
22829         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22830             node.parentNode.removeChild(node);
22831             return;
22832         }
22833         
22834         // remove - but keep children..
22835         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22836             while (node.childNodes.length) {
22837                 var cn = node.childNodes[0];
22838                 node.removeChild(cn);
22839                 node.parentNode.insertBefore(cn, node);
22840             }
22841             node.parentNode.removeChild(node);
22842             this.iterateChildren(node, this.cleanWord);
22843             return;
22844         }
22845         // clean styles
22846         if (node.className.length) {
22847             
22848             var cn = node.className.split(/\W+/);
22849             var cna = [];
22850             Roo.each(cn, function(cls) {
22851                 if (cls.match(/Mso[a-zA-Z]+/)) {
22852                     return;
22853                 }
22854                 cna.push(cls);
22855             });
22856             node.className = cna.length ? cna.join(' ') : '';
22857             if (!cna.length) {
22858                 node.removeAttribute("class");
22859             }
22860         }
22861         
22862         if (node.hasAttribute("lang")) {
22863             node.removeAttribute("lang");
22864         }
22865         
22866         if (node.hasAttribute("style")) {
22867             
22868             var styles = node.getAttribute("style").split(";");
22869             var nstyle = [];
22870             Roo.each(styles, function(s) {
22871                 if (!s.match(/:/)) {
22872                     return;
22873                 }
22874                 var kv = s.split(":");
22875                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22876                     return;
22877                 }
22878                 // what ever is left... we allow.
22879                 nstyle.push(s);
22880             });
22881             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22882             if (!nstyle.length) {
22883                 node.removeAttribute('style');
22884             }
22885         }
22886         this.iterateChildren(node, this.cleanWord);
22887         
22888         
22889         
22890     },
22891     /**
22892      * iterateChildren of a Node, calling fn each time, using this as the scole..
22893      * @param {DomNode} node node to iterate children of.
22894      * @param {Function} fn method of this class to call on each item.
22895      */
22896     iterateChildren : function(node, fn)
22897     {
22898         if (!node.childNodes.length) {
22899                 return;
22900         }
22901         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22902            fn.call(this, node.childNodes[i])
22903         }
22904     },
22905     
22906     
22907     /**
22908      * cleanTableWidths.
22909      *
22910      * Quite often pasting from word etc.. results in tables with column and widths.
22911      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22912      *
22913      */
22914     cleanTableWidths : function(node)
22915     {
22916          
22917          
22918         if (!node) {
22919             this.cleanTableWidths(this.doc.body);
22920             return;
22921         }
22922         
22923         // ignore list...
22924         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22925             return; 
22926         }
22927         Roo.log(node.tagName);
22928         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22929             this.iterateChildren(node, this.cleanTableWidths);
22930             return;
22931         }
22932         if (node.hasAttribute('width')) {
22933             node.removeAttribute('width');
22934         }
22935         
22936          
22937         if (node.hasAttribute("style")) {
22938             // pretty basic...
22939             
22940             var styles = node.getAttribute("style").split(";");
22941             var nstyle = [];
22942             Roo.each(styles, function(s) {
22943                 if (!s.match(/:/)) {
22944                     return;
22945                 }
22946                 var kv = s.split(":");
22947                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22948                     return;
22949                 }
22950                 // what ever is left... we allow.
22951                 nstyle.push(s);
22952             });
22953             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22954             if (!nstyle.length) {
22955                 node.removeAttribute('style');
22956             }
22957         }
22958         
22959         this.iterateChildren(node, this.cleanTableWidths);
22960         
22961         
22962     },
22963     
22964     
22965     
22966     
22967     domToHTML : function(currentElement, depth, nopadtext) {
22968         
22969         depth = depth || 0;
22970         nopadtext = nopadtext || false;
22971     
22972         if (!currentElement) {
22973             return this.domToHTML(this.doc.body);
22974         }
22975         
22976         //Roo.log(currentElement);
22977         var j;
22978         var allText = false;
22979         var nodeName = currentElement.nodeName;
22980         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22981         
22982         if  (nodeName == '#text') {
22983             
22984             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22985         }
22986         
22987         
22988         var ret = '';
22989         if (nodeName != 'BODY') {
22990              
22991             var i = 0;
22992             // Prints the node tagName, such as <A>, <IMG>, etc
22993             if (tagName) {
22994                 var attr = [];
22995                 for(i = 0; i < currentElement.attributes.length;i++) {
22996                     // quoting?
22997                     var aname = currentElement.attributes.item(i).name;
22998                     if (!currentElement.attributes.item(i).value.length) {
22999                         continue;
23000                     }
23001                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23002                 }
23003                 
23004                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23005             } 
23006             else {
23007                 
23008                 // eack
23009             }
23010         } else {
23011             tagName = false;
23012         }
23013         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23014             return ret;
23015         }
23016         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23017             nopadtext = true;
23018         }
23019         
23020         
23021         // Traverse the tree
23022         i = 0;
23023         var currentElementChild = currentElement.childNodes.item(i);
23024         var allText = true;
23025         var innerHTML  = '';
23026         lastnode = '';
23027         while (currentElementChild) {
23028             // Formatting code (indent the tree so it looks nice on the screen)
23029             var nopad = nopadtext;
23030             if (lastnode == 'SPAN') {
23031                 nopad  = true;
23032             }
23033             // text
23034             if  (currentElementChild.nodeName == '#text') {
23035                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23036                 toadd = nopadtext ? toadd : toadd.trim();
23037                 if (!nopad && toadd.length > 80) {
23038                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23039                 }
23040                 innerHTML  += toadd;
23041                 
23042                 i++;
23043                 currentElementChild = currentElement.childNodes.item(i);
23044                 lastNode = '';
23045                 continue;
23046             }
23047             allText = false;
23048             
23049             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23050                 
23051             // Recursively traverse the tree structure of the child node
23052             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23053             lastnode = currentElementChild.nodeName;
23054             i++;
23055             currentElementChild=currentElement.childNodes.item(i);
23056         }
23057         
23058         ret += innerHTML;
23059         
23060         if (!allText) {
23061                 // The remaining code is mostly for formatting the tree
23062             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23063         }
23064         
23065         
23066         if (tagName) {
23067             ret+= "</"+tagName+">";
23068         }
23069         return ret;
23070         
23071     },
23072         
23073     applyBlacklists : function()
23074     {
23075         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23076         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23077         
23078         this.white = [];
23079         this.black = [];
23080         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23081             if (b.indexOf(tag) > -1) {
23082                 return;
23083             }
23084             this.white.push(tag);
23085             
23086         }, this);
23087         
23088         Roo.each(w, function(tag) {
23089             if (b.indexOf(tag) > -1) {
23090                 return;
23091             }
23092             if (this.white.indexOf(tag) > -1) {
23093                 return;
23094             }
23095             this.white.push(tag);
23096             
23097         }, this);
23098         
23099         
23100         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23101             if (w.indexOf(tag) > -1) {
23102                 return;
23103             }
23104             this.black.push(tag);
23105             
23106         }, this);
23107         
23108         Roo.each(b, function(tag) {
23109             if (w.indexOf(tag) > -1) {
23110                 return;
23111             }
23112             if (this.black.indexOf(tag) > -1) {
23113                 return;
23114             }
23115             this.black.push(tag);
23116             
23117         }, this);
23118         
23119         
23120         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23121         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23122         
23123         this.cwhite = [];
23124         this.cblack = [];
23125         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23126             if (b.indexOf(tag) > -1) {
23127                 return;
23128             }
23129             this.cwhite.push(tag);
23130             
23131         }, this);
23132         
23133         Roo.each(w, function(tag) {
23134             if (b.indexOf(tag) > -1) {
23135                 return;
23136             }
23137             if (this.cwhite.indexOf(tag) > -1) {
23138                 return;
23139             }
23140             this.cwhite.push(tag);
23141             
23142         }, this);
23143         
23144         
23145         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23146             if (w.indexOf(tag) > -1) {
23147                 return;
23148             }
23149             this.cblack.push(tag);
23150             
23151         }, this);
23152         
23153         Roo.each(b, function(tag) {
23154             if (w.indexOf(tag) > -1) {
23155                 return;
23156             }
23157             if (this.cblack.indexOf(tag) > -1) {
23158                 return;
23159             }
23160             this.cblack.push(tag);
23161             
23162         }, this);
23163     },
23164     
23165     setStylesheets : function(stylesheets)
23166     {
23167         if(typeof(stylesheets) == 'string'){
23168             Roo.get(this.iframe.contentDocument.head).createChild({
23169                 tag : 'link',
23170                 rel : 'stylesheet',
23171                 type : 'text/css',
23172                 href : stylesheets
23173             });
23174             
23175             return;
23176         }
23177         var _this = this;
23178      
23179         Roo.each(stylesheets, function(s) {
23180             if(!s.length){
23181                 return;
23182             }
23183             
23184             Roo.get(_this.iframe.contentDocument.head).createChild({
23185                 tag : 'link',
23186                 rel : 'stylesheet',
23187                 type : 'text/css',
23188                 href : s
23189             });
23190         });
23191
23192         
23193     },
23194     
23195     removeStylesheets : function()
23196     {
23197         var _this = this;
23198         
23199         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23200             s.remove();
23201         });
23202     },
23203     
23204     setStyle : function(style)
23205     {
23206         Roo.get(this.iframe.contentDocument.head).createChild({
23207             tag : 'style',
23208             type : 'text/css',
23209             html : style
23210         });
23211
23212         return;
23213     }
23214     
23215     // hide stuff that is not compatible
23216     /**
23217      * @event blur
23218      * @hide
23219      */
23220     /**
23221      * @event change
23222      * @hide
23223      */
23224     /**
23225      * @event focus
23226      * @hide
23227      */
23228     /**
23229      * @event specialkey
23230      * @hide
23231      */
23232     /**
23233      * @cfg {String} fieldClass @hide
23234      */
23235     /**
23236      * @cfg {String} focusClass @hide
23237      */
23238     /**
23239      * @cfg {String} autoCreate @hide
23240      */
23241     /**
23242      * @cfg {String} inputType @hide
23243      */
23244     /**
23245      * @cfg {String} invalidClass @hide
23246      */
23247     /**
23248      * @cfg {String} invalidText @hide
23249      */
23250     /**
23251      * @cfg {String} msgFx @hide
23252      */
23253     /**
23254      * @cfg {String} validateOnBlur @hide
23255      */
23256 });
23257
23258 Roo.HtmlEditorCore.white = [
23259         'area', 'br', 'img', 'input', 'hr', 'wbr',
23260         
23261        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23262        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23263        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23264        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23265        'table',   'ul',         'xmp', 
23266        
23267        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23268       'thead',   'tr', 
23269      
23270       'dir', 'menu', 'ol', 'ul', 'dl',
23271        
23272       'embed',  'object'
23273 ];
23274
23275
23276 Roo.HtmlEditorCore.black = [
23277     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23278         'applet', // 
23279         'base',   'basefont', 'bgsound', 'blink',  'body', 
23280         'frame',  'frameset', 'head',    'html',   'ilayer', 
23281         'iframe', 'layer',  'link',     'meta',    'object',   
23282         'script', 'style' ,'title',  'xml' // clean later..
23283 ];
23284 Roo.HtmlEditorCore.clean = [
23285     'script', 'style', 'title', 'xml'
23286 ];
23287 Roo.HtmlEditorCore.remove = [
23288     'font'
23289 ];
23290 // attributes..
23291
23292 Roo.HtmlEditorCore.ablack = [
23293     'on'
23294 ];
23295     
23296 Roo.HtmlEditorCore.aclean = [ 
23297     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23298 ];
23299
23300 // protocols..
23301 Roo.HtmlEditorCore.pwhite= [
23302         'http',  'https',  'mailto'
23303 ];
23304
23305 // white listed style attributes.
23306 Roo.HtmlEditorCore.cwhite= [
23307       //  'text-align', /// default is to allow most things..
23308       
23309          
23310 //        'font-size'//??
23311 ];
23312
23313 // black listed style attributes.
23314 Roo.HtmlEditorCore.cblack= [
23315       //  'font-size' -- this can be set by the project 
23316 ];
23317
23318
23319 Roo.HtmlEditorCore.swapCodes   =[ 
23320     [    8211, "--" ], 
23321     [    8212, "--" ], 
23322     [    8216,  "'" ],  
23323     [    8217, "'" ],  
23324     [    8220, '"' ],  
23325     [    8221, '"' ],  
23326     [    8226, "*" ],  
23327     [    8230, "..." ]
23328 ]; 
23329
23330     /*
23331  * - LGPL
23332  *
23333  * HtmlEditor
23334  * 
23335  */
23336
23337 /**
23338  * @class Roo.bootstrap.HtmlEditor
23339  * @extends Roo.bootstrap.TextArea
23340  * Bootstrap HtmlEditor class
23341
23342  * @constructor
23343  * Create a new HtmlEditor
23344  * @param {Object} config The config object
23345  */
23346
23347 Roo.bootstrap.HtmlEditor = function(config){
23348     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23349     if (!this.toolbars) {
23350         this.toolbars = [];
23351     }
23352     
23353     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23354     this.addEvents({
23355             /**
23356              * @event initialize
23357              * Fires when the editor is fully initialized (including the iframe)
23358              * @param {HtmlEditor} this
23359              */
23360             initialize: true,
23361             /**
23362              * @event activate
23363              * Fires when the editor is first receives the focus. Any insertion must wait
23364              * until after this event.
23365              * @param {HtmlEditor} this
23366              */
23367             activate: true,
23368              /**
23369              * @event beforesync
23370              * Fires before the textarea is updated with content from the editor iframe. Return false
23371              * to cancel the sync.
23372              * @param {HtmlEditor} this
23373              * @param {String} html
23374              */
23375             beforesync: true,
23376              /**
23377              * @event beforepush
23378              * Fires before the iframe editor is updated with content from the textarea. Return false
23379              * to cancel the push.
23380              * @param {HtmlEditor} this
23381              * @param {String} html
23382              */
23383             beforepush: true,
23384              /**
23385              * @event sync
23386              * Fires when the textarea is updated with content from the editor iframe.
23387              * @param {HtmlEditor} this
23388              * @param {String} html
23389              */
23390             sync: true,
23391              /**
23392              * @event push
23393              * Fires when the iframe editor is updated with content from the textarea.
23394              * @param {HtmlEditor} this
23395              * @param {String} html
23396              */
23397             push: true,
23398              /**
23399              * @event editmodechange
23400              * Fires when the editor switches edit modes
23401              * @param {HtmlEditor} this
23402              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23403              */
23404             editmodechange: true,
23405             /**
23406              * @event editorevent
23407              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23408              * @param {HtmlEditor} this
23409              */
23410             editorevent: true,
23411             /**
23412              * @event firstfocus
23413              * Fires when on first focus - needed by toolbars..
23414              * @param {HtmlEditor} this
23415              */
23416             firstfocus: true,
23417             /**
23418              * @event autosave
23419              * Auto save the htmlEditor value as a file into Events
23420              * @param {HtmlEditor} this
23421              */
23422             autosave: true,
23423             /**
23424              * @event savedpreview
23425              * preview the saved version of htmlEditor
23426              * @param {HtmlEditor} this
23427              */
23428             savedpreview: true
23429         });
23430 };
23431
23432
23433 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23434     
23435     
23436       /**
23437      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23438      */
23439     toolbars : false,
23440     
23441      /**
23442     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23443     */
23444     btns : [],
23445    
23446      /**
23447      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23448      *                        Roo.resizable.
23449      */
23450     resizable : false,
23451      /**
23452      * @cfg {Number} height (in pixels)
23453      */   
23454     height: 300,
23455    /**
23456      * @cfg {Number} width (in pixels)
23457      */   
23458     width: false,
23459     
23460     /**
23461      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23462      * 
23463      */
23464     stylesheets: false,
23465     
23466     // id of frame..
23467     frameId: false,
23468     
23469     // private properties
23470     validationEvent : false,
23471     deferHeight: true,
23472     initialized : false,
23473     activated : false,
23474     
23475     onFocus : Roo.emptyFn,
23476     iframePad:3,
23477     hideMode:'offsets',
23478     
23479     tbContainer : false,
23480     
23481     bodyCls : '',
23482     
23483     toolbarContainer :function() {
23484         return this.wrap.select('.x-html-editor-tb',true).first();
23485     },
23486
23487     /**
23488      * Protected method that will not generally be called directly. It
23489      * is called when the editor creates its toolbar. Override this method if you need to
23490      * add custom toolbar buttons.
23491      * @param {HtmlEditor} editor
23492      */
23493     createToolbar : function(){
23494         Roo.log('renewing');
23495         Roo.log("create toolbars");
23496         
23497         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23498         this.toolbars[0].render(this.toolbarContainer());
23499         
23500         return;
23501         
23502 //        if (!editor.toolbars || !editor.toolbars.length) {
23503 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23504 //        }
23505 //        
23506 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23507 //            editor.toolbars[i] = Roo.factory(
23508 //                    typeof(editor.toolbars[i]) == 'string' ?
23509 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23510 //                Roo.bootstrap.HtmlEditor);
23511 //            editor.toolbars[i].init(editor);
23512 //        }
23513     },
23514
23515      
23516     // private
23517     onRender : function(ct, position)
23518     {
23519        // Roo.log("Call onRender: " + this.xtype);
23520         var _t = this;
23521         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23522       
23523         this.wrap = this.inputEl().wrap({
23524             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23525         });
23526         
23527         this.editorcore.onRender(ct, position);
23528          
23529         if (this.resizable) {
23530             this.resizeEl = new Roo.Resizable(this.wrap, {
23531                 pinned : true,
23532                 wrap: true,
23533                 dynamic : true,
23534                 minHeight : this.height,
23535                 height: this.height,
23536                 handles : this.resizable,
23537                 width: this.width,
23538                 listeners : {
23539                     resize : function(r, w, h) {
23540                         _t.onResize(w,h); // -something
23541                     }
23542                 }
23543             });
23544             
23545         }
23546         this.createToolbar(this);
23547        
23548         
23549         if(!this.width && this.resizable){
23550             this.setSize(this.wrap.getSize());
23551         }
23552         if (this.resizeEl) {
23553             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23554             // should trigger onReize..
23555         }
23556         
23557     },
23558
23559     // private
23560     onResize : function(w, h)
23561     {
23562         Roo.log('resize: ' +w + ',' + h );
23563         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23564         var ew = false;
23565         var eh = false;
23566         
23567         if(this.inputEl() ){
23568             if(typeof w == 'number'){
23569                 var aw = w - this.wrap.getFrameWidth('lr');
23570                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23571                 ew = aw;
23572             }
23573             if(typeof h == 'number'){
23574                  var tbh = -11;  // fixme it needs to tool bar size!
23575                 for (var i =0; i < this.toolbars.length;i++) {
23576                     // fixme - ask toolbars for heights?
23577                     tbh += this.toolbars[i].el.getHeight();
23578                     //if (this.toolbars[i].footer) {
23579                     //    tbh += this.toolbars[i].footer.el.getHeight();
23580                     //}
23581                 }
23582               
23583                 
23584                 
23585                 
23586                 
23587                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23588                 ah -= 5; // knock a few pixes off for look..
23589                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23590                 var eh = ah;
23591             }
23592         }
23593         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23594         this.editorcore.onResize(ew,eh);
23595         
23596     },
23597
23598     /**
23599      * Toggles the editor between standard and source edit mode.
23600      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23601      */
23602     toggleSourceEdit : function(sourceEditMode)
23603     {
23604         this.editorcore.toggleSourceEdit(sourceEditMode);
23605         
23606         if(this.editorcore.sourceEditMode){
23607             Roo.log('editor - showing textarea');
23608             
23609 //            Roo.log('in');
23610 //            Roo.log(this.syncValue());
23611             this.syncValue();
23612             this.inputEl().removeClass(['hide', 'x-hidden']);
23613             this.inputEl().dom.removeAttribute('tabIndex');
23614             this.inputEl().focus();
23615         }else{
23616             Roo.log('editor - hiding textarea');
23617 //            Roo.log('out')
23618 //            Roo.log(this.pushValue()); 
23619             this.pushValue();
23620             
23621             this.inputEl().addClass(['hide', 'x-hidden']);
23622             this.inputEl().dom.setAttribute('tabIndex', -1);
23623             //this.deferFocus();
23624         }
23625          
23626         if(this.resizable){
23627             this.setSize(this.wrap.getSize());
23628         }
23629         
23630         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23631     },
23632  
23633     // private (for BoxComponent)
23634     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23635
23636     // private (for BoxComponent)
23637     getResizeEl : function(){
23638         return this.wrap;
23639     },
23640
23641     // private (for BoxComponent)
23642     getPositionEl : function(){
23643         return this.wrap;
23644     },
23645
23646     // private
23647     initEvents : function(){
23648         this.originalValue = this.getValue();
23649     },
23650
23651 //    /**
23652 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23653 //     * @method
23654 //     */
23655 //    markInvalid : Roo.emptyFn,
23656 //    /**
23657 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23658 //     * @method
23659 //     */
23660 //    clearInvalid : Roo.emptyFn,
23661
23662     setValue : function(v){
23663         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23664         this.editorcore.pushValue();
23665     },
23666
23667      
23668     // private
23669     deferFocus : function(){
23670         this.focus.defer(10, this);
23671     },
23672
23673     // doc'ed in Field
23674     focus : function(){
23675         this.editorcore.focus();
23676         
23677     },
23678       
23679
23680     // private
23681     onDestroy : function(){
23682         
23683         
23684         
23685         if(this.rendered){
23686             
23687             for (var i =0; i < this.toolbars.length;i++) {
23688                 // fixme - ask toolbars for heights?
23689                 this.toolbars[i].onDestroy();
23690             }
23691             
23692             this.wrap.dom.innerHTML = '';
23693             this.wrap.remove();
23694         }
23695     },
23696
23697     // private
23698     onFirstFocus : function(){
23699         //Roo.log("onFirstFocus");
23700         this.editorcore.onFirstFocus();
23701          for (var i =0; i < this.toolbars.length;i++) {
23702             this.toolbars[i].onFirstFocus();
23703         }
23704         
23705     },
23706     
23707     // private
23708     syncValue : function()
23709     {   
23710         this.editorcore.syncValue();
23711     },
23712     
23713     pushValue : function()
23714     {   
23715         this.editorcore.pushValue();
23716     }
23717      
23718     
23719     // hide stuff that is not compatible
23720     /**
23721      * @event blur
23722      * @hide
23723      */
23724     /**
23725      * @event change
23726      * @hide
23727      */
23728     /**
23729      * @event focus
23730      * @hide
23731      */
23732     /**
23733      * @event specialkey
23734      * @hide
23735      */
23736     /**
23737      * @cfg {String} fieldClass @hide
23738      */
23739     /**
23740      * @cfg {String} focusClass @hide
23741      */
23742     /**
23743      * @cfg {String} autoCreate @hide
23744      */
23745     /**
23746      * @cfg {String} inputType @hide
23747      */
23748     /**
23749      * @cfg {String} invalidClass @hide
23750      */
23751     /**
23752      * @cfg {String} invalidText @hide
23753      */
23754     /**
23755      * @cfg {String} msgFx @hide
23756      */
23757     /**
23758      * @cfg {String} validateOnBlur @hide
23759      */
23760 });
23761  
23762     
23763    
23764    
23765    
23766       
23767 Roo.namespace('Roo.bootstrap.htmleditor');
23768 /**
23769  * @class Roo.bootstrap.HtmlEditorToolbar1
23770  * Basic Toolbar
23771  * 
23772  * Usage:
23773  *
23774  new Roo.bootstrap.HtmlEditor({
23775     ....
23776     toolbars : [
23777         new Roo.bootstrap.HtmlEditorToolbar1({
23778             disable : { fonts: 1 , format: 1, ..., ... , ...],
23779             btns : [ .... ]
23780         })
23781     }
23782      
23783  * 
23784  * @cfg {Object} disable List of elements to disable..
23785  * @cfg {Array} btns List of additional buttons.
23786  * 
23787  * 
23788  * NEEDS Extra CSS? 
23789  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23790  */
23791  
23792 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23793 {
23794     
23795     Roo.apply(this, config);
23796     
23797     // default disabled, based on 'good practice'..
23798     this.disable = this.disable || {};
23799     Roo.applyIf(this.disable, {
23800         fontSize : true,
23801         colors : true,
23802         specialElements : true
23803     });
23804     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23805     
23806     this.editor = config.editor;
23807     this.editorcore = config.editor.editorcore;
23808     
23809     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23810     
23811     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23812     // dont call parent... till later.
23813 }
23814 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23815      
23816     bar : true,
23817     
23818     editor : false,
23819     editorcore : false,
23820     
23821     
23822     formats : [
23823         "p" ,  
23824         "h1","h2","h3","h4","h5","h6", 
23825         "pre", "code", 
23826         "abbr", "acronym", "address", "cite", "samp", "var",
23827         'div','span'
23828     ],
23829     
23830     onRender : function(ct, position)
23831     {
23832        // Roo.log("Call onRender: " + this.xtype);
23833         
23834        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23835        Roo.log(this.el);
23836        this.el.dom.style.marginBottom = '0';
23837        var _this = this;
23838        var editorcore = this.editorcore;
23839        var editor= this.editor;
23840        
23841        var children = [];
23842        var btn = function(id,cmd , toggle, handler, html){
23843        
23844             var  event = toggle ? 'toggle' : 'click';
23845        
23846             var a = {
23847                 size : 'sm',
23848                 xtype: 'Button',
23849                 xns: Roo.bootstrap,
23850                 glyphicon : id,
23851                 cmd : id || cmd,
23852                 enableToggle:toggle !== false,
23853                 html : html || '',
23854                 pressed : toggle ? false : null,
23855                 listeners : {}
23856             };
23857             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23858                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23859             };
23860             children.push(a);
23861             return a;
23862        }
23863        
23864     //    var cb_box = function...
23865         
23866         var style = {
23867                 xtype: 'Button',
23868                 size : 'sm',
23869                 xns: Roo.bootstrap,
23870                 glyphicon : 'font',
23871                 //html : 'submit'
23872                 menu : {
23873                     xtype: 'Menu',
23874                     xns: Roo.bootstrap,
23875                     items:  []
23876                 }
23877         };
23878         Roo.each(this.formats, function(f) {
23879             style.menu.items.push({
23880                 xtype :'MenuItem',
23881                 xns: Roo.bootstrap,
23882                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23883                 tagname : f,
23884                 listeners : {
23885                     click : function()
23886                     {
23887                         editorcore.insertTag(this.tagname);
23888                         editor.focus();
23889                     }
23890                 }
23891                 
23892             });
23893         });
23894         children.push(style);   
23895         
23896         btn('bold',false,true);
23897         btn('italic',false,true);
23898         btn('align-left', 'justifyleft',true);
23899         btn('align-center', 'justifycenter',true);
23900         btn('align-right' , 'justifyright',true);
23901         btn('link', false, false, function(btn) {
23902             //Roo.log("create link?");
23903             var url = prompt(this.createLinkText, this.defaultLinkValue);
23904             if(url && url != 'http:/'+'/'){
23905                 this.editorcore.relayCmd('createlink', url);
23906             }
23907         }),
23908         btn('list','insertunorderedlist',true);
23909         btn('pencil', false,true, function(btn){
23910                 Roo.log(this);
23911                 this.toggleSourceEdit(btn.pressed);
23912         });
23913         
23914         if (this.editor.btns.length > 0) {
23915             for (var i = 0; i<this.editor.btns.length; i++) {
23916                 children.push(this.editor.btns[i]);
23917             }
23918         }
23919         
23920         /*
23921         var cog = {
23922                 xtype: 'Button',
23923                 size : 'sm',
23924                 xns: Roo.bootstrap,
23925                 glyphicon : 'cog',
23926                 //html : 'submit'
23927                 menu : {
23928                     xtype: 'Menu',
23929                     xns: Roo.bootstrap,
23930                     items:  []
23931                 }
23932         };
23933         
23934         cog.menu.items.push({
23935             xtype :'MenuItem',
23936             xns: Roo.bootstrap,
23937             html : Clean styles,
23938             tagname : f,
23939             listeners : {
23940                 click : function()
23941                 {
23942                     editorcore.insertTag(this.tagname);
23943                     editor.focus();
23944                 }
23945             }
23946             
23947         });
23948        */
23949         
23950          
23951        this.xtype = 'NavSimplebar';
23952         
23953         for(var i=0;i< children.length;i++) {
23954             
23955             this.buttons.add(this.addxtypeChild(children[i]));
23956             
23957         }
23958         
23959         editor.on('editorevent', this.updateToolbar, this);
23960     },
23961     onBtnClick : function(id)
23962     {
23963        this.editorcore.relayCmd(id);
23964        this.editorcore.focus();
23965     },
23966     
23967     /**
23968      * Protected method that will not generally be called directly. It triggers
23969      * a toolbar update by reading the markup state of the current selection in the editor.
23970      */
23971     updateToolbar: function(){
23972
23973         if(!this.editorcore.activated){
23974             this.editor.onFirstFocus(); // is this neeed?
23975             return;
23976         }
23977
23978         var btns = this.buttons; 
23979         var doc = this.editorcore.doc;
23980         btns.get('bold').setActive(doc.queryCommandState('bold'));
23981         btns.get('italic').setActive(doc.queryCommandState('italic'));
23982         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23983         
23984         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23985         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23986         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23987         
23988         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23989         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23990          /*
23991         
23992         var ans = this.editorcore.getAllAncestors();
23993         if (this.formatCombo) {
23994             
23995             
23996             var store = this.formatCombo.store;
23997             this.formatCombo.setValue("");
23998             for (var i =0; i < ans.length;i++) {
23999                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24000                     // select it..
24001                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24002                     break;
24003                 }
24004             }
24005         }
24006         
24007         
24008         
24009         // hides menus... - so this cant be on a menu...
24010         Roo.bootstrap.MenuMgr.hideAll();
24011         */
24012         Roo.bootstrap.MenuMgr.hideAll();
24013         //this.editorsyncValue();
24014     },
24015     onFirstFocus: function() {
24016         this.buttons.each(function(item){
24017            item.enable();
24018         });
24019     },
24020     toggleSourceEdit : function(sourceEditMode){
24021         
24022           
24023         if(sourceEditMode){
24024             Roo.log("disabling buttons");
24025            this.buttons.each( function(item){
24026                 if(item.cmd != 'pencil'){
24027                     item.disable();
24028                 }
24029             });
24030           
24031         }else{
24032             Roo.log("enabling buttons");
24033             if(this.editorcore.initialized){
24034                 this.buttons.each( function(item){
24035                     item.enable();
24036                 });
24037             }
24038             
24039         }
24040         Roo.log("calling toggole on editor");
24041         // tell the editor that it's been pressed..
24042         this.editor.toggleSourceEdit(sourceEditMode);
24043        
24044     }
24045 });
24046
24047
24048
24049
24050
24051 /**
24052  * @class Roo.bootstrap.Table.AbstractSelectionModel
24053  * @extends Roo.util.Observable
24054  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24055  * implemented by descendant classes.  This class should not be directly instantiated.
24056  * @constructor
24057  */
24058 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24059     this.locked = false;
24060     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24061 };
24062
24063
24064 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24065     /** @ignore Called by the grid automatically. Do not call directly. */
24066     init : function(grid){
24067         this.grid = grid;
24068         this.initEvents();
24069     },
24070
24071     /**
24072      * Locks the selections.
24073      */
24074     lock : function(){
24075         this.locked = true;
24076     },
24077
24078     /**
24079      * Unlocks the selections.
24080      */
24081     unlock : function(){
24082         this.locked = false;
24083     },
24084
24085     /**
24086      * Returns true if the selections are locked.
24087      * @return {Boolean}
24088      */
24089     isLocked : function(){
24090         return this.locked;
24091     }
24092 });
24093 /**
24094  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24095  * @class Roo.bootstrap.Table.RowSelectionModel
24096  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24097  * It supports multiple selections and keyboard selection/navigation. 
24098  * @constructor
24099  * @param {Object} config
24100  */
24101
24102 Roo.bootstrap.Table.RowSelectionModel = function(config){
24103     Roo.apply(this, config);
24104     this.selections = new Roo.util.MixedCollection(false, function(o){
24105         return o.id;
24106     });
24107
24108     this.last = false;
24109     this.lastActive = false;
24110
24111     this.addEvents({
24112         /**
24113              * @event selectionchange
24114              * Fires when the selection changes
24115              * @param {SelectionModel} this
24116              */
24117             "selectionchange" : true,
24118         /**
24119              * @event afterselectionchange
24120              * Fires after the selection changes (eg. by key press or clicking)
24121              * @param {SelectionModel} this
24122              */
24123             "afterselectionchange" : true,
24124         /**
24125              * @event beforerowselect
24126              * Fires when a row is selected being selected, return false to cancel.
24127              * @param {SelectionModel} this
24128              * @param {Number} rowIndex The selected index
24129              * @param {Boolean} keepExisting False if other selections will be cleared
24130              */
24131             "beforerowselect" : true,
24132         /**
24133              * @event rowselect
24134              * Fires when a row is selected.
24135              * @param {SelectionModel} this
24136              * @param {Number} rowIndex The selected index
24137              * @param {Roo.data.Record} r The record
24138              */
24139             "rowselect" : true,
24140         /**
24141              * @event rowdeselect
24142              * Fires when a row is deselected.
24143              * @param {SelectionModel} this
24144              * @param {Number} rowIndex The selected index
24145              */
24146         "rowdeselect" : true
24147     });
24148     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24149     this.locked = false;
24150  };
24151
24152 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24153     /**
24154      * @cfg {Boolean} singleSelect
24155      * True to allow selection of only one row at a time (defaults to false)
24156      */
24157     singleSelect : false,
24158
24159     // private
24160     initEvents : function()
24161     {
24162
24163         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24164         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24165         //}else{ // allow click to work like normal
24166          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24167         //}
24168         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24169         this.grid.on("rowclick", this.handleMouseDown, this);
24170         
24171         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24172             "up" : function(e){
24173                 if(!e.shiftKey){
24174                     this.selectPrevious(e.shiftKey);
24175                 }else if(this.last !== false && this.lastActive !== false){
24176                     var last = this.last;
24177                     this.selectRange(this.last,  this.lastActive-1);
24178                     this.grid.getView().focusRow(this.lastActive);
24179                     if(last !== false){
24180                         this.last = last;
24181                     }
24182                 }else{
24183                     this.selectFirstRow();
24184                 }
24185                 this.fireEvent("afterselectionchange", this);
24186             },
24187             "down" : function(e){
24188                 if(!e.shiftKey){
24189                     this.selectNext(e.shiftKey);
24190                 }else if(this.last !== false && this.lastActive !== false){
24191                     var last = this.last;
24192                     this.selectRange(this.last,  this.lastActive+1);
24193                     this.grid.getView().focusRow(this.lastActive);
24194                     if(last !== false){
24195                         this.last = last;
24196                     }
24197                 }else{
24198                     this.selectFirstRow();
24199                 }
24200                 this.fireEvent("afterselectionchange", this);
24201             },
24202             scope: this
24203         });
24204         this.grid.store.on('load', function(){
24205             this.selections.clear();
24206         },this);
24207         /*
24208         var view = this.grid.view;
24209         view.on("refresh", this.onRefresh, this);
24210         view.on("rowupdated", this.onRowUpdated, this);
24211         view.on("rowremoved", this.onRemove, this);
24212         */
24213     },
24214
24215     // private
24216     onRefresh : function()
24217     {
24218         var ds = this.grid.store, i, v = this.grid.view;
24219         var s = this.selections;
24220         s.each(function(r){
24221             if((i = ds.indexOfId(r.id)) != -1){
24222                 v.onRowSelect(i);
24223             }else{
24224                 s.remove(r);
24225             }
24226         });
24227     },
24228
24229     // private
24230     onRemove : function(v, index, r){
24231         this.selections.remove(r);
24232     },
24233
24234     // private
24235     onRowUpdated : function(v, index, r){
24236         if(this.isSelected(r)){
24237             v.onRowSelect(index);
24238         }
24239     },
24240
24241     /**
24242      * Select records.
24243      * @param {Array} records The records to select
24244      * @param {Boolean} keepExisting (optional) True to keep existing selections
24245      */
24246     selectRecords : function(records, keepExisting)
24247     {
24248         if(!keepExisting){
24249             this.clearSelections();
24250         }
24251             var ds = this.grid.store;
24252         for(var i = 0, len = records.length; i < len; i++){
24253             this.selectRow(ds.indexOf(records[i]), true);
24254         }
24255     },
24256
24257     /**
24258      * Gets the number of selected rows.
24259      * @return {Number}
24260      */
24261     getCount : function(){
24262         return this.selections.length;
24263     },
24264
24265     /**
24266      * Selects the first row in the grid.
24267      */
24268     selectFirstRow : function(){
24269         this.selectRow(0);
24270     },
24271
24272     /**
24273      * Select the last row.
24274      * @param {Boolean} keepExisting (optional) True to keep existing selections
24275      */
24276     selectLastRow : function(keepExisting){
24277         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24278         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24279     },
24280
24281     /**
24282      * Selects the row immediately following the last selected row.
24283      * @param {Boolean} keepExisting (optional) True to keep existing selections
24284      */
24285     selectNext : function(keepExisting)
24286     {
24287             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24288             this.selectRow(this.last+1, keepExisting);
24289             this.grid.getView().focusRow(this.last);
24290         }
24291     },
24292
24293     /**
24294      * Selects the row that precedes the last selected row.
24295      * @param {Boolean} keepExisting (optional) True to keep existing selections
24296      */
24297     selectPrevious : function(keepExisting){
24298         if(this.last){
24299             this.selectRow(this.last-1, keepExisting);
24300             this.grid.getView().focusRow(this.last);
24301         }
24302     },
24303
24304     /**
24305      * Returns the selected records
24306      * @return {Array} Array of selected records
24307      */
24308     getSelections : function(){
24309         return [].concat(this.selections.items);
24310     },
24311
24312     /**
24313      * Returns the first selected record.
24314      * @return {Record}
24315      */
24316     getSelected : function(){
24317         return this.selections.itemAt(0);
24318     },
24319
24320
24321     /**
24322      * Clears all selections.
24323      */
24324     clearSelections : function(fast)
24325     {
24326         if(this.locked) {
24327             return;
24328         }
24329         if(fast !== true){
24330                 var ds = this.grid.store;
24331             var s = this.selections;
24332             s.each(function(r){
24333                 this.deselectRow(ds.indexOfId(r.id));
24334             }, this);
24335             s.clear();
24336         }else{
24337             this.selections.clear();
24338         }
24339         this.last = false;
24340     },
24341
24342
24343     /**
24344      * Selects all rows.
24345      */
24346     selectAll : function(){
24347         if(this.locked) {
24348             return;
24349         }
24350         this.selections.clear();
24351         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24352             this.selectRow(i, true);
24353         }
24354     },
24355
24356     /**
24357      * Returns True if there is a selection.
24358      * @return {Boolean}
24359      */
24360     hasSelection : function(){
24361         return this.selections.length > 0;
24362     },
24363
24364     /**
24365      * Returns True if the specified row is selected.
24366      * @param {Number/Record} record The record or index of the record to check
24367      * @return {Boolean}
24368      */
24369     isSelected : function(index){
24370             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24371         return (r && this.selections.key(r.id) ? true : false);
24372     },
24373
24374     /**
24375      * Returns True if the specified record id is selected.
24376      * @param {String} id The id of record to check
24377      * @return {Boolean}
24378      */
24379     isIdSelected : function(id){
24380         return (this.selections.key(id) ? true : false);
24381     },
24382
24383
24384     // private
24385     handleMouseDBClick : function(e, t){
24386         
24387     },
24388     // private
24389     handleMouseDown : function(e, t)
24390     {
24391             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24392         if(this.isLocked() || rowIndex < 0 ){
24393             return;
24394         };
24395         if(e.shiftKey && this.last !== false){
24396             var last = this.last;
24397             this.selectRange(last, rowIndex, e.ctrlKey);
24398             this.last = last; // reset the last
24399             t.focus();
24400     
24401         }else{
24402             var isSelected = this.isSelected(rowIndex);
24403             //Roo.log("select row:" + rowIndex);
24404             if(isSelected){
24405                 this.deselectRow(rowIndex);
24406             } else {
24407                         this.selectRow(rowIndex, true);
24408             }
24409     
24410             /*
24411                 if(e.button !== 0 && isSelected){
24412                 alert('rowIndex 2: ' + rowIndex);
24413                     view.focusRow(rowIndex);
24414                 }else if(e.ctrlKey && isSelected){
24415                     this.deselectRow(rowIndex);
24416                 }else if(!isSelected){
24417                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24418                     view.focusRow(rowIndex);
24419                 }
24420             */
24421         }
24422         this.fireEvent("afterselectionchange", this);
24423     },
24424     // private
24425     handleDragableRowClick :  function(grid, rowIndex, e) 
24426     {
24427         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24428             this.selectRow(rowIndex, false);
24429             grid.view.focusRow(rowIndex);
24430              this.fireEvent("afterselectionchange", this);
24431         }
24432     },
24433     
24434     /**
24435      * Selects multiple rows.
24436      * @param {Array} rows Array of the indexes of the row to select
24437      * @param {Boolean} keepExisting (optional) True to keep existing selections
24438      */
24439     selectRows : function(rows, keepExisting){
24440         if(!keepExisting){
24441             this.clearSelections();
24442         }
24443         for(var i = 0, len = rows.length; i < len; i++){
24444             this.selectRow(rows[i], true);
24445         }
24446     },
24447
24448     /**
24449      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24450      * @param {Number} startRow The index of the first row in the range
24451      * @param {Number} endRow The index of the last row in the range
24452      * @param {Boolean} keepExisting (optional) True to retain existing selections
24453      */
24454     selectRange : function(startRow, endRow, keepExisting){
24455         if(this.locked) {
24456             return;
24457         }
24458         if(!keepExisting){
24459             this.clearSelections();
24460         }
24461         if(startRow <= endRow){
24462             for(var i = startRow; i <= endRow; i++){
24463                 this.selectRow(i, true);
24464             }
24465         }else{
24466             for(var i = startRow; i >= endRow; i--){
24467                 this.selectRow(i, true);
24468             }
24469         }
24470     },
24471
24472     /**
24473      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24474      * @param {Number} startRow The index of the first row in the range
24475      * @param {Number} endRow The index of the last row in the range
24476      */
24477     deselectRange : function(startRow, endRow, preventViewNotify){
24478         if(this.locked) {
24479             return;
24480         }
24481         for(var i = startRow; i <= endRow; i++){
24482             this.deselectRow(i, preventViewNotify);
24483         }
24484     },
24485
24486     /**
24487      * Selects a row.
24488      * @param {Number} row The index of the row to select
24489      * @param {Boolean} keepExisting (optional) True to keep existing selections
24490      */
24491     selectRow : function(index, keepExisting, preventViewNotify)
24492     {
24493             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24494             return;
24495         }
24496         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24497             if(!keepExisting || this.singleSelect){
24498                 this.clearSelections();
24499             }
24500             
24501             var r = this.grid.store.getAt(index);
24502             //console.log('selectRow - record id :' + r.id);
24503             
24504             this.selections.add(r);
24505             this.last = this.lastActive = index;
24506             if(!preventViewNotify){
24507                 var proxy = new Roo.Element(
24508                                 this.grid.getRowDom(index)
24509                 );
24510                 proxy.addClass('bg-info info');
24511             }
24512             this.fireEvent("rowselect", this, index, r);
24513             this.fireEvent("selectionchange", this);
24514         }
24515     },
24516
24517     /**
24518      * Deselects a row.
24519      * @param {Number} row The index of the row to deselect
24520      */
24521     deselectRow : function(index, preventViewNotify)
24522     {
24523         if(this.locked) {
24524             return;
24525         }
24526         if(this.last == index){
24527             this.last = false;
24528         }
24529         if(this.lastActive == index){
24530             this.lastActive = false;
24531         }
24532         
24533         var r = this.grid.store.getAt(index);
24534         if (!r) {
24535             return;
24536         }
24537         
24538         this.selections.remove(r);
24539         //.console.log('deselectRow - record id :' + r.id);
24540         if(!preventViewNotify){
24541         
24542             var proxy = new Roo.Element(
24543                 this.grid.getRowDom(index)
24544             );
24545             proxy.removeClass('bg-info info');
24546         }
24547         this.fireEvent("rowdeselect", this, index);
24548         this.fireEvent("selectionchange", this);
24549     },
24550
24551     // private
24552     restoreLast : function(){
24553         if(this._last){
24554             this.last = this._last;
24555         }
24556     },
24557
24558     // private
24559     acceptsNav : function(row, col, cm){
24560         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24561     },
24562
24563     // private
24564     onEditorKey : function(field, e){
24565         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24566         if(k == e.TAB){
24567             e.stopEvent();
24568             ed.completeEdit();
24569             if(e.shiftKey){
24570                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24571             }else{
24572                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24573             }
24574         }else if(k == e.ENTER && !e.ctrlKey){
24575             e.stopEvent();
24576             ed.completeEdit();
24577             if(e.shiftKey){
24578                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24579             }else{
24580                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24581             }
24582         }else if(k == e.ESC){
24583             ed.cancelEdit();
24584         }
24585         if(newCell){
24586             g.startEditing(newCell[0], newCell[1]);
24587         }
24588     }
24589 });
24590 /*
24591  * Based on:
24592  * Ext JS Library 1.1.1
24593  * Copyright(c) 2006-2007, Ext JS, LLC.
24594  *
24595  * Originally Released Under LGPL - original licence link has changed is not relivant.
24596  *
24597  * Fork - LGPL
24598  * <script type="text/javascript">
24599  */
24600  
24601 /**
24602  * @class Roo.bootstrap.PagingToolbar
24603  * @extends Roo.bootstrap.NavSimplebar
24604  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24605  * @constructor
24606  * Create a new PagingToolbar
24607  * @param {Object} config The config object
24608  * @param {Roo.data.Store} store
24609  */
24610 Roo.bootstrap.PagingToolbar = function(config)
24611 {
24612     // old args format still supported... - xtype is prefered..
24613         // created from xtype...
24614     
24615     this.ds = config.dataSource;
24616     
24617     if (config.store && !this.ds) {
24618         this.store= Roo.factory(config.store, Roo.data);
24619         this.ds = this.store;
24620         this.ds.xmodule = this.xmodule || false;
24621     }
24622     
24623     this.toolbarItems = [];
24624     if (config.items) {
24625         this.toolbarItems = config.items;
24626     }
24627     
24628     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24629     
24630     this.cursor = 0;
24631     
24632     if (this.ds) { 
24633         this.bind(this.ds);
24634     }
24635     
24636     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24637     
24638 };
24639
24640 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24641     /**
24642      * @cfg {Roo.data.Store} dataSource
24643      * The underlying data store providing the paged data
24644      */
24645     /**
24646      * @cfg {String/HTMLElement/Element} container
24647      * container The id or element that will contain the toolbar
24648      */
24649     /**
24650      * @cfg {Boolean} displayInfo
24651      * True to display the displayMsg (defaults to false)
24652      */
24653     /**
24654      * @cfg {Number} pageSize
24655      * The number of records to display per page (defaults to 20)
24656      */
24657     pageSize: 20,
24658     /**
24659      * @cfg {String} displayMsg
24660      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24661      */
24662     displayMsg : 'Displaying {0} - {1} of {2}',
24663     /**
24664      * @cfg {String} emptyMsg
24665      * The message to display when no records are found (defaults to "No data to display")
24666      */
24667     emptyMsg : 'No data to display',
24668     /**
24669      * Customizable piece of the default paging text (defaults to "Page")
24670      * @type String
24671      */
24672     beforePageText : "Page",
24673     /**
24674      * Customizable piece of the default paging text (defaults to "of %0")
24675      * @type String
24676      */
24677     afterPageText : "of {0}",
24678     /**
24679      * Customizable piece of the default paging text (defaults to "First Page")
24680      * @type String
24681      */
24682     firstText : "First Page",
24683     /**
24684      * Customizable piece of the default paging text (defaults to "Previous Page")
24685      * @type String
24686      */
24687     prevText : "Previous Page",
24688     /**
24689      * Customizable piece of the default paging text (defaults to "Next Page")
24690      * @type String
24691      */
24692     nextText : "Next Page",
24693     /**
24694      * Customizable piece of the default paging text (defaults to "Last Page")
24695      * @type String
24696      */
24697     lastText : "Last Page",
24698     /**
24699      * Customizable piece of the default paging text (defaults to "Refresh")
24700      * @type String
24701      */
24702     refreshText : "Refresh",
24703
24704     buttons : false,
24705     // private
24706     onRender : function(ct, position) 
24707     {
24708         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24709         this.navgroup.parentId = this.id;
24710         this.navgroup.onRender(this.el, null);
24711         // add the buttons to the navgroup
24712         
24713         if(this.displayInfo){
24714             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24715             this.displayEl = this.el.select('.x-paging-info', true).first();
24716 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24717 //            this.displayEl = navel.el.select('span',true).first();
24718         }
24719         
24720         var _this = this;
24721         
24722         if(this.buttons){
24723             Roo.each(_this.buttons, function(e){ // this might need to use render????
24724                Roo.factory(e).render(_this.el);
24725             });
24726         }
24727             
24728         Roo.each(_this.toolbarItems, function(e) {
24729             _this.navgroup.addItem(e);
24730         });
24731         
24732         
24733         this.first = this.navgroup.addItem({
24734             tooltip: this.firstText,
24735             cls: "prev",
24736             icon : 'fa fa-backward',
24737             disabled: true,
24738             preventDefault: true,
24739             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24740         });
24741         
24742         this.prev =  this.navgroup.addItem({
24743             tooltip: this.prevText,
24744             cls: "prev",
24745             icon : 'fa fa-step-backward',
24746             disabled: true,
24747             preventDefault: true,
24748             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24749         });
24750     //this.addSeparator();
24751         
24752         
24753         var field = this.navgroup.addItem( {
24754             tagtype : 'span',
24755             cls : 'x-paging-position',
24756             
24757             html : this.beforePageText  +
24758                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24759                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24760          } ); //?? escaped?
24761         
24762         this.field = field.el.select('input', true).first();
24763         this.field.on("keydown", this.onPagingKeydown, this);
24764         this.field.on("focus", function(){this.dom.select();});
24765     
24766     
24767         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24768         //this.field.setHeight(18);
24769         //this.addSeparator();
24770         this.next = this.navgroup.addItem({
24771             tooltip: this.nextText,
24772             cls: "next",
24773             html : ' <i class="fa fa-step-forward">',
24774             disabled: true,
24775             preventDefault: true,
24776             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24777         });
24778         this.last = this.navgroup.addItem({
24779             tooltip: this.lastText,
24780             icon : 'fa fa-forward',
24781             cls: "next",
24782             disabled: true,
24783             preventDefault: true,
24784             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24785         });
24786     //this.addSeparator();
24787         this.loading = this.navgroup.addItem({
24788             tooltip: this.refreshText,
24789             icon: 'fa fa-refresh',
24790             preventDefault: true,
24791             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24792         });
24793         
24794     },
24795
24796     // private
24797     updateInfo : function(){
24798         if(this.displayEl){
24799             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24800             var msg = count == 0 ?
24801                 this.emptyMsg :
24802                 String.format(
24803                     this.displayMsg,
24804                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24805                 );
24806             this.displayEl.update(msg);
24807         }
24808     },
24809
24810     // private
24811     onLoad : function(ds, r, o)
24812     {
24813         this.cursor = o.params.start ? o.params.start : 0;
24814         
24815         var d = this.getPageData(),
24816             ap = d.activePage,
24817             ps = d.pages;
24818         
24819         
24820         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24821         this.field.dom.value = ap;
24822         this.first.setDisabled(ap == 1);
24823         this.prev.setDisabled(ap == 1);
24824         this.next.setDisabled(ap == ps);
24825         this.last.setDisabled(ap == ps);
24826         this.loading.enable();
24827         this.updateInfo();
24828     },
24829
24830     // private
24831     getPageData : function(){
24832         var total = this.ds.getTotalCount();
24833         return {
24834             total : total,
24835             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24836             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24837         };
24838     },
24839
24840     // private
24841     onLoadError : function(){
24842         this.loading.enable();
24843     },
24844
24845     // private
24846     onPagingKeydown : function(e){
24847         var k = e.getKey();
24848         var d = this.getPageData();
24849         if(k == e.RETURN){
24850             var v = this.field.dom.value, pageNum;
24851             if(!v || isNaN(pageNum = parseInt(v, 10))){
24852                 this.field.dom.value = d.activePage;
24853                 return;
24854             }
24855             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24856             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24857             e.stopEvent();
24858         }
24859         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))
24860         {
24861           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24862           this.field.dom.value = pageNum;
24863           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24864           e.stopEvent();
24865         }
24866         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24867         {
24868           var v = this.field.dom.value, pageNum; 
24869           var increment = (e.shiftKey) ? 10 : 1;
24870           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24871                 increment *= -1;
24872           }
24873           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24874             this.field.dom.value = d.activePage;
24875             return;
24876           }
24877           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24878           {
24879             this.field.dom.value = parseInt(v, 10) + increment;
24880             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24881             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24882           }
24883           e.stopEvent();
24884         }
24885     },
24886
24887     // private
24888     beforeLoad : function(){
24889         if(this.loading){
24890             this.loading.disable();
24891         }
24892     },
24893
24894     // private
24895     onClick : function(which){
24896         
24897         var ds = this.ds;
24898         if (!ds) {
24899             return;
24900         }
24901         
24902         switch(which){
24903             case "first":
24904                 ds.load({params:{start: 0, limit: this.pageSize}});
24905             break;
24906             case "prev":
24907                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24908             break;
24909             case "next":
24910                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24911             break;
24912             case "last":
24913                 var total = ds.getTotalCount();
24914                 var extra = total % this.pageSize;
24915                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24916                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24917             break;
24918             case "refresh":
24919                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24920             break;
24921         }
24922     },
24923
24924     /**
24925      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24926      * @param {Roo.data.Store} store The data store to unbind
24927      */
24928     unbind : function(ds){
24929         ds.un("beforeload", this.beforeLoad, this);
24930         ds.un("load", this.onLoad, this);
24931         ds.un("loadexception", this.onLoadError, this);
24932         ds.un("remove", this.updateInfo, this);
24933         ds.un("add", this.updateInfo, this);
24934         this.ds = undefined;
24935     },
24936
24937     /**
24938      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24939      * @param {Roo.data.Store} store The data store to bind
24940      */
24941     bind : function(ds){
24942         ds.on("beforeload", this.beforeLoad, this);
24943         ds.on("load", this.onLoad, this);
24944         ds.on("loadexception", this.onLoadError, this);
24945         ds.on("remove", this.updateInfo, this);
24946         ds.on("add", this.updateInfo, this);
24947         this.ds = ds;
24948     }
24949 });/*
24950  * - LGPL
24951  *
24952  * element
24953  * 
24954  */
24955
24956 /**
24957  * @class Roo.bootstrap.MessageBar
24958  * @extends Roo.bootstrap.Component
24959  * Bootstrap MessageBar class
24960  * @cfg {String} html contents of the MessageBar
24961  * @cfg {String} weight (info | success | warning | danger) default info
24962  * @cfg {String} beforeClass insert the bar before the given class
24963  * @cfg {Boolean} closable (true | false) default false
24964  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24965  * 
24966  * @constructor
24967  * Create a new Element
24968  * @param {Object} config The config object
24969  */
24970
24971 Roo.bootstrap.MessageBar = function(config){
24972     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24973 };
24974
24975 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24976     
24977     html: '',
24978     weight: 'info',
24979     closable: false,
24980     fixed: false,
24981     beforeClass: 'bootstrap-sticky-wrap',
24982     
24983     getAutoCreate : function(){
24984         
24985         var cfg = {
24986             tag: 'div',
24987             cls: 'alert alert-dismissable alert-' + this.weight,
24988             cn: [
24989                 {
24990                     tag: 'span',
24991                     cls: 'message',
24992                     html: this.html || ''
24993                 }
24994             ]
24995         };
24996         
24997         if(this.fixed){
24998             cfg.cls += ' alert-messages-fixed';
24999         }
25000         
25001         if(this.closable){
25002             cfg.cn.push({
25003                 tag: 'button',
25004                 cls: 'close',
25005                 html: 'x'
25006             });
25007         }
25008         
25009         return cfg;
25010     },
25011     
25012     onRender : function(ct, position)
25013     {
25014         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25015         
25016         if(!this.el){
25017             var cfg = Roo.apply({},  this.getAutoCreate());
25018             cfg.id = Roo.id();
25019             
25020             if (this.cls) {
25021                 cfg.cls += ' ' + this.cls;
25022             }
25023             if (this.style) {
25024                 cfg.style = this.style;
25025             }
25026             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25027             
25028             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25029         }
25030         
25031         this.el.select('>button.close').on('click', this.hide, this);
25032         
25033     },
25034     
25035     show : function()
25036     {
25037         if (!this.rendered) {
25038             this.render();
25039         }
25040         
25041         this.el.show();
25042         
25043         this.fireEvent('show', this);
25044         
25045     },
25046     
25047     hide : function()
25048     {
25049         if (!this.rendered) {
25050             this.render();
25051         }
25052         
25053         this.el.hide();
25054         
25055         this.fireEvent('hide', this);
25056     },
25057     
25058     update : function()
25059     {
25060 //        var e = this.el.dom.firstChild;
25061 //        
25062 //        if(this.closable){
25063 //            e = e.nextSibling;
25064 //        }
25065 //        
25066 //        e.data = this.html || '';
25067
25068         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25069     }
25070    
25071 });
25072
25073  
25074
25075      /*
25076  * - LGPL
25077  *
25078  * Graph
25079  * 
25080  */
25081
25082
25083 /**
25084  * @class Roo.bootstrap.Graph
25085  * @extends Roo.bootstrap.Component
25086  * Bootstrap Graph class
25087 > Prameters
25088  -sm {number} sm 4
25089  -md {number} md 5
25090  @cfg {String} graphtype  bar | vbar | pie
25091  @cfg {number} g_x coodinator | centre x (pie)
25092  @cfg {number} g_y coodinator | centre y (pie)
25093  @cfg {number} g_r radius (pie)
25094  @cfg {number} g_height height of the chart (respected by all elements in the set)
25095  @cfg {number} g_width width of the chart (respected by all elements in the set)
25096  @cfg {Object} title The title of the chart
25097     
25098  -{Array}  values
25099  -opts (object) options for the chart 
25100      o {
25101      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25102      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25103      o vgutter (number)
25104      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.
25105      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25106      o to
25107      o stretch (boolean)
25108      o }
25109  -opts (object) options for the pie
25110      o{
25111      o cut
25112      o startAngle (number)
25113      o endAngle (number)
25114      } 
25115  *
25116  * @constructor
25117  * Create a new Input
25118  * @param {Object} config The config object
25119  */
25120
25121 Roo.bootstrap.Graph = function(config){
25122     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25123     
25124     this.addEvents({
25125         // img events
25126         /**
25127          * @event click
25128          * The img click event for the img.
25129          * @param {Roo.EventObject} e
25130          */
25131         "click" : true
25132     });
25133 };
25134
25135 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25136     
25137     sm: 4,
25138     md: 5,
25139     graphtype: 'bar',
25140     g_height: 250,
25141     g_width: 400,
25142     g_x: 50,
25143     g_y: 50,
25144     g_r: 30,
25145     opts:{
25146         //g_colors: this.colors,
25147         g_type: 'soft',
25148         g_gutter: '20%'
25149
25150     },
25151     title : false,
25152
25153     getAutoCreate : function(){
25154         
25155         var cfg = {
25156             tag: 'div',
25157             html : null
25158         };
25159         
25160         
25161         return  cfg;
25162     },
25163
25164     onRender : function(ct,position){
25165         
25166         
25167         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25168         
25169         if (typeof(Raphael) == 'undefined') {
25170             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25171             return;
25172         }
25173         
25174         this.raphael = Raphael(this.el.dom);
25175         
25176                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25177                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25178                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25179                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25180                 /*
25181                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25182                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25183                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25184                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25185                 
25186                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25187                 r.barchart(330, 10, 300, 220, data1);
25188                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25189                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25190                 */
25191                 
25192                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25193                 // r.barchart(30, 30, 560, 250,  xdata, {
25194                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25195                 //     axis : "0 0 1 1",
25196                 //     axisxlabels :  xdata
25197                 //     //yvalues : cols,
25198                    
25199                 // });
25200 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25201 //        
25202 //        this.load(null,xdata,{
25203 //                axis : "0 0 1 1",
25204 //                axisxlabels :  xdata
25205 //                });
25206
25207     },
25208
25209     load : function(graphtype,xdata,opts)
25210     {
25211         this.raphael.clear();
25212         if(!graphtype) {
25213             graphtype = this.graphtype;
25214         }
25215         if(!opts){
25216             opts = this.opts;
25217         }
25218         var r = this.raphael,
25219             fin = function () {
25220                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25221             },
25222             fout = function () {
25223                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25224             },
25225             pfin = function() {
25226                 this.sector.stop();
25227                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25228
25229                 if (this.label) {
25230                     this.label[0].stop();
25231                     this.label[0].attr({ r: 7.5 });
25232                     this.label[1].attr({ "font-weight": 800 });
25233                 }
25234             },
25235             pfout = function() {
25236                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25237
25238                 if (this.label) {
25239                     this.label[0].animate({ r: 5 }, 500, "bounce");
25240                     this.label[1].attr({ "font-weight": 400 });
25241                 }
25242             };
25243
25244         switch(graphtype){
25245             case 'bar':
25246                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25247                 break;
25248             case 'hbar':
25249                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25250                 break;
25251             case 'pie':
25252 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25253 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25254 //            
25255                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25256                 
25257                 break;
25258
25259         }
25260         
25261         if(this.title){
25262             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25263         }
25264         
25265     },
25266     
25267     setTitle: function(o)
25268     {
25269         this.title = o;
25270     },
25271     
25272     initEvents: function() {
25273         
25274         if(!this.href){
25275             this.el.on('click', this.onClick, this);
25276         }
25277     },
25278     
25279     onClick : function(e)
25280     {
25281         Roo.log('img onclick');
25282         this.fireEvent('click', this, e);
25283     }
25284    
25285 });
25286
25287  
25288 /*
25289  * - LGPL
25290  *
25291  * numberBox
25292  * 
25293  */
25294 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25295
25296 /**
25297  * @class Roo.bootstrap.dash.NumberBox
25298  * @extends Roo.bootstrap.Component
25299  * Bootstrap NumberBox class
25300  * @cfg {String} headline Box headline
25301  * @cfg {String} content Box content
25302  * @cfg {String} icon Box icon
25303  * @cfg {String} footer Footer text
25304  * @cfg {String} fhref Footer href
25305  * 
25306  * @constructor
25307  * Create a new NumberBox
25308  * @param {Object} config The config object
25309  */
25310
25311
25312 Roo.bootstrap.dash.NumberBox = function(config){
25313     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25314     
25315 };
25316
25317 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25318     
25319     headline : '',
25320     content : '',
25321     icon : '',
25322     footer : '',
25323     fhref : '',
25324     ficon : '',
25325     
25326     getAutoCreate : function(){
25327         
25328         var cfg = {
25329             tag : 'div',
25330             cls : 'small-box ',
25331             cn : [
25332                 {
25333                     tag : 'div',
25334                     cls : 'inner',
25335                     cn :[
25336                         {
25337                             tag : 'h3',
25338                             cls : 'roo-headline',
25339                             html : this.headline
25340                         },
25341                         {
25342                             tag : 'p',
25343                             cls : 'roo-content',
25344                             html : this.content
25345                         }
25346                     ]
25347                 }
25348             ]
25349         };
25350         
25351         if(this.icon){
25352             cfg.cn.push({
25353                 tag : 'div',
25354                 cls : 'icon',
25355                 cn :[
25356                     {
25357                         tag : 'i',
25358                         cls : 'ion ' + this.icon
25359                     }
25360                 ]
25361             });
25362         }
25363         
25364         if(this.footer){
25365             var footer = {
25366                 tag : 'a',
25367                 cls : 'small-box-footer',
25368                 href : this.fhref || '#',
25369                 html : this.footer
25370             };
25371             
25372             cfg.cn.push(footer);
25373             
25374         }
25375         
25376         return  cfg;
25377     },
25378
25379     onRender : function(ct,position){
25380         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25381
25382
25383        
25384                 
25385     },
25386
25387     setHeadline: function (value)
25388     {
25389         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25390     },
25391     
25392     setFooter: function (value, href)
25393     {
25394         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25395         
25396         if(href){
25397             this.el.select('a.small-box-footer',true).first().attr('href', href);
25398         }
25399         
25400     },
25401
25402     setContent: function (value)
25403     {
25404         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25405     },
25406
25407     initEvents: function() 
25408     {   
25409         
25410     }
25411     
25412 });
25413
25414  
25415 /*
25416  * - LGPL
25417  *
25418  * TabBox
25419  * 
25420  */
25421 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25422
25423 /**
25424  * @class Roo.bootstrap.dash.TabBox
25425  * @extends Roo.bootstrap.Component
25426  * Bootstrap TabBox class
25427  * @cfg {String} title Title of the TabBox
25428  * @cfg {String} icon Icon of the TabBox
25429  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25430  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25431  * 
25432  * @constructor
25433  * Create a new TabBox
25434  * @param {Object} config The config object
25435  */
25436
25437
25438 Roo.bootstrap.dash.TabBox = function(config){
25439     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25440     this.addEvents({
25441         // raw events
25442         /**
25443          * @event addpane
25444          * When a pane is added
25445          * @param {Roo.bootstrap.dash.TabPane} pane
25446          */
25447         "addpane" : true,
25448         /**
25449          * @event activatepane
25450          * When a pane is activated
25451          * @param {Roo.bootstrap.dash.TabPane} pane
25452          */
25453         "activatepane" : true
25454         
25455          
25456     });
25457     
25458     this.panes = [];
25459 };
25460
25461 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25462
25463     title : '',
25464     icon : false,
25465     showtabs : true,
25466     tabScrollable : false,
25467     
25468     getChildContainer : function()
25469     {
25470         return this.el.select('.tab-content', true).first();
25471     },
25472     
25473     getAutoCreate : function(){
25474         
25475         var header = {
25476             tag: 'li',
25477             cls: 'pull-left header',
25478             html: this.title,
25479             cn : []
25480         };
25481         
25482         if(this.icon){
25483             header.cn.push({
25484                 tag: 'i',
25485                 cls: 'fa ' + this.icon
25486             });
25487         }
25488         
25489         var h = {
25490             tag: 'ul',
25491             cls: 'nav nav-tabs pull-right',
25492             cn: [
25493                 header
25494             ]
25495         };
25496         
25497         if(this.tabScrollable){
25498             h = {
25499                 tag: 'div',
25500                 cls: 'tab-header',
25501                 cn: [
25502                     {
25503                         tag: 'ul',
25504                         cls: 'nav nav-tabs pull-right',
25505                         cn: [
25506                             header
25507                         ]
25508                     }
25509                 ]
25510             };
25511         }
25512         
25513         var cfg = {
25514             tag: 'div',
25515             cls: 'nav-tabs-custom',
25516             cn: [
25517                 h,
25518                 {
25519                     tag: 'div',
25520                     cls: 'tab-content no-padding',
25521                     cn: []
25522                 }
25523             ]
25524         };
25525
25526         return  cfg;
25527     },
25528     initEvents : function()
25529     {
25530         //Roo.log('add add pane handler');
25531         this.on('addpane', this.onAddPane, this);
25532     },
25533      /**
25534      * Updates the box title
25535      * @param {String} html to set the title to.
25536      */
25537     setTitle : function(value)
25538     {
25539         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25540     },
25541     onAddPane : function(pane)
25542     {
25543         this.panes.push(pane);
25544         //Roo.log('addpane');
25545         //Roo.log(pane);
25546         // tabs are rendere left to right..
25547         if(!this.showtabs){
25548             return;
25549         }
25550         
25551         var ctr = this.el.select('.nav-tabs', true).first();
25552          
25553          
25554         var existing = ctr.select('.nav-tab',true);
25555         var qty = existing.getCount();;
25556         
25557         
25558         var tab = ctr.createChild({
25559             tag : 'li',
25560             cls : 'nav-tab' + (qty ? '' : ' active'),
25561             cn : [
25562                 {
25563                     tag : 'a',
25564                     href:'#',
25565                     html : pane.title
25566                 }
25567             ]
25568         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25569         pane.tab = tab;
25570         
25571         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25572         if (!qty) {
25573             pane.el.addClass('active');
25574         }
25575         
25576                 
25577     },
25578     onTabClick : function(ev,un,ob,pane)
25579     {
25580         //Roo.log('tab - prev default');
25581         ev.preventDefault();
25582         
25583         
25584         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25585         pane.tab.addClass('active');
25586         //Roo.log(pane.title);
25587         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25588         // technically we should have a deactivate event.. but maybe add later.
25589         // and it should not de-activate the selected tab...
25590         this.fireEvent('activatepane', pane);
25591         pane.el.addClass('active');
25592         pane.fireEvent('activate');
25593         
25594         
25595     },
25596     
25597     getActivePane : function()
25598     {
25599         var r = false;
25600         Roo.each(this.panes, function(p) {
25601             if(p.el.hasClass('active')){
25602                 r = p;
25603                 return false;
25604             }
25605             
25606             return;
25607         });
25608         
25609         return r;
25610     }
25611     
25612     
25613 });
25614
25615  
25616 /*
25617  * - LGPL
25618  *
25619  * Tab pane
25620  * 
25621  */
25622 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25623 /**
25624  * @class Roo.bootstrap.TabPane
25625  * @extends Roo.bootstrap.Component
25626  * Bootstrap TabPane class
25627  * @cfg {Boolean} active (false | true) Default false
25628  * @cfg {String} title title of panel
25629
25630  * 
25631  * @constructor
25632  * Create a new TabPane
25633  * @param {Object} config The config object
25634  */
25635
25636 Roo.bootstrap.dash.TabPane = function(config){
25637     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25638     
25639     this.addEvents({
25640         // raw events
25641         /**
25642          * @event activate
25643          * When a pane is activated
25644          * @param {Roo.bootstrap.dash.TabPane} pane
25645          */
25646         "activate" : true
25647          
25648     });
25649 };
25650
25651 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25652     
25653     active : false,
25654     title : '',
25655     
25656     // the tabBox that this is attached to.
25657     tab : false,
25658      
25659     getAutoCreate : function() 
25660     {
25661         var cfg = {
25662             tag: 'div',
25663             cls: 'tab-pane'
25664         };
25665         
25666         if(this.active){
25667             cfg.cls += ' active';
25668         }
25669         
25670         return cfg;
25671     },
25672     initEvents  : function()
25673     {
25674         //Roo.log('trigger add pane handler');
25675         this.parent().fireEvent('addpane', this)
25676     },
25677     
25678      /**
25679      * Updates the tab title 
25680      * @param {String} html to set the title to.
25681      */
25682     setTitle: function(str)
25683     {
25684         if (!this.tab) {
25685             return;
25686         }
25687         this.title = str;
25688         this.tab.select('a', true).first().dom.innerHTML = str;
25689         
25690     }
25691     
25692     
25693     
25694 });
25695
25696  
25697
25698
25699  /*
25700  * - LGPL
25701  *
25702  * menu
25703  * 
25704  */
25705 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25706
25707 /**
25708  * @class Roo.bootstrap.menu.Menu
25709  * @extends Roo.bootstrap.Component
25710  * Bootstrap Menu class - container for Menu
25711  * @cfg {String} html Text of the menu
25712  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25713  * @cfg {String} icon Font awesome icon
25714  * @cfg {String} pos Menu align to (top | bottom) default bottom
25715  * 
25716  * 
25717  * @constructor
25718  * Create a new Menu
25719  * @param {Object} config The config object
25720  */
25721
25722
25723 Roo.bootstrap.menu.Menu = function(config){
25724     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25725     
25726     this.addEvents({
25727         /**
25728          * @event beforeshow
25729          * Fires before this menu is displayed
25730          * @param {Roo.bootstrap.menu.Menu} this
25731          */
25732         beforeshow : true,
25733         /**
25734          * @event beforehide
25735          * Fires before this menu is hidden
25736          * @param {Roo.bootstrap.menu.Menu} this
25737          */
25738         beforehide : true,
25739         /**
25740          * @event show
25741          * Fires after this menu is displayed
25742          * @param {Roo.bootstrap.menu.Menu} this
25743          */
25744         show : true,
25745         /**
25746          * @event hide
25747          * Fires after this menu is hidden
25748          * @param {Roo.bootstrap.menu.Menu} this
25749          */
25750         hide : true,
25751         /**
25752          * @event click
25753          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25754          * @param {Roo.bootstrap.menu.Menu} this
25755          * @param {Roo.EventObject} e
25756          */
25757         click : true
25758     });
25759     
25760 };
25761
25762 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25763     
25764     submenu : false,
25765     html : '',
25766     weight : 'default',
25767     icon : false,
25768     pos : 'bottom',
25769     
25770     
25771     getChildContainer : function() {
25772         if(this.isSubMenu){
25773             return this.el;
25774         }
25775         
25776         return this.el.select('ul.dropdown-menu', true).first();  
25777     },
25778     
25779     getAutoCreate : function()
25780     {
25781         var text = [
25782             {
25783                 tag : 'span',
25784                 cls : 'roo-menu-text',
25785                 html : this.html
25786             }
25787         ];
25788         
25789         if(this.icon){
25790             text.unshift({
25791                 tag : 'i',
25792                 cls : 'fa ' + this.icon
25793             })
25794         }
25795         
25796         
25797         var cfg = {
25798             tag : 'div',
25799             cls : 'btn-group',
25800             cn : [
25801                 {
25802                     tag : 'button',
25803                     cls : 'dropdown-button btn btn-' + this.weight,
25804                     cn : text
25805                 },
25806                 {
25807                     tag : 'button',
25808                     cls : 'dropdown-toggle btn btn-' + this.weight,
25809                     cn : [
25810                         {
25811                             tag : 'span',
25812                             cls : 'caret'
25813                         }
25814                     ]
25815                 },
25816                 {
25817                     tag : 'ul',
25818                     cls : 'dropdown-menu'
25819                 }
25820             ]
25821             
25822         };
25823         
25824         if(this.pos == 'top'){
25825             cfg.cls += ' dropup';
25826         }
25827         
25828         if(this.isSubMenu){
25829             cfg = {
25830                 tag : 'ul',
25831                 cls : 'dropdown-menu'
25832             }
25833         }
25834         
25835         return cfg;
25836     },
25837     
25838     onRender : function(ct, position)
25839     {
25840         this.isSubMenu = ct.hasClass('dropdown-submenu');
25841         
25842         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25843     },
25844     
25845     initEvents : function() 
25846     {
25847         if(this.isSubMenu){
25848             return;
25849         }
25850         
25851         this.hidden = true;
25852         
25853         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25854         this.triggerEl.on('click', this.onTriggerPress, this);
25855         
25856         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25857         this.buttonEl.on('click', this.onClick, this);
25858         
25859     },
25860     
25861     list : function()
25862     {
25863         if(this.isSubMenu){
25864             return this.el;
25865         }
25866         
25867         return this.el.select('ul.dropdown-menu', true).first();
25868     },
25869     
25870     onClick : function(e)
25871     {
25872         this.fireEvent("click", this, e);
25873     },
25874     
25875     onTriggerPress  : function(e)
25876     {   
25877         if (this.isVisible()) {
25878             this.hide();
25879         } else {
25880             this.show();
25881         }
25882     },
25883     
25884     isVisible : function(){
25885         return !this.hidden;
25886     },
25887     
25888     show : function()
25889     {
25890         this.fireEvent("beforeshow", this);
25891         
25892         this.hidden = false;
25893         this.el.addClass('open');
25894         
25895         Roo.get(document).on("mouseup", this.onMouseUp, this);
25896         
25897         this.fireEvent("show", this);
25898         
25899         
25900     },
25901     
25902     hide : function()
25903     {
25904         this.fireEvent("beforehide", this);
25905         
25906         this.hidden = true;
25907         this.el.removeClass('open');
25908         
25909         Roo.get(document).un("mouseup", this.onMouseUp);
25910         
25911         this.fireEvent("hide", this);
25912     },
25913     
25914     onMouseUp : function()
25915     {
25916         this.hide();
25917     }
25918     
25919 });
25920
25921  
25922  /*
25923  * - LGPL
25924  *
25925  * menu item
25926  * 
25927  */
25928 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25929
25930 /**
25931  * @class Roo.bootstrap.menu.Item
25932  * @extends Roo.bootstrap.Component
25933  * Bootstrap MenuItem class
25934  * @cfg {Boolean} submenu (true | false) default false
25935  * @cfg {String} html text of the item
25936  * @cfg {String} href the link
25937  * @cfg {Boolean} disable (true | false) default false
25938  * @cfg {Boolean} preventDefault (true | false) default true
25939  * @cfg {String} icon Font awesome icon
25940  * @cfg {String} pos Submenu align to (left | right) default right 
25941  * 
25942  * 
25943  * @constructor
25944  * Create a new Item
25945  * @param {Object} config The config object
25946  */
25947
25948
25949 Roo.bootstrap.menu.Item = function(config){
25950     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25951     this.addEvents({
25952         /**
25953          * @event mouseover
25954          * Fires when the mouse is hovering over this menu
25955          * @param {Roo.bootstrap.menu.Item} this
25956          * @param {Roo.EventObject} e
25957          */
25958         mouseover : true,
25959         /**
25960          * @event mouseout
25961          * Fires when the mouse exits this menu
25962          * @param {Roo.bootstrap.menu.Item} this
25963          * @param {Roo.EventObject} e
25964          */
25965         mouseout : true,
25966         // raw events
25967         /**
25968          * @event click
25969          * The raw click event for the entire grid.
25970          * @param {Roo.EventObject} e
25971          */
25972         click : true
25973     });
25974 };
25975
25976 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25977     
25978     submenu : false,
25979     href : '',
25980     html : '',
25981     preventDefault: true,
25982     disable : false,
25983     icon : false,
25984     pos : 'right',
25985     
25986     getAutoCreate : function()
25987     {
25988         var text = [
25989             {
25990                 tag : 'span',
25991                 cls : 'roo-menu-item-text',
25992                 html : this.html
25993             }
25994         ];
25995         
25996         if(this.icon){
25997             text.unshift({
25998                 tag : 'i',
25999                 cls : 'fa ' + this.icon
26000             })
26001         }
26002         
26003         var cfg = {
26004             tag : 'li',
26005             cn : [
26006                 {
26007                     tag : 'a',
26008                     href : this.href || '#',
26009                     cn : text
26010                 }
26011             ]
26012         };
26013         
26014         if(this.disable){
26015             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26016         }
26017         
26018         if(this.submenu){
26019             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26020             
26021             if(this.pos == 'left'){
26022                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26023             }
26024         }
26025         
26026         return cfg;
26027     },
26028     
26029     initEvents : function() 
26030     {
26031         this.el.on('mouseover', this.onMouseOver, this);
26032         this.el.on('mouseout', this.onMouseOut, this);
26033         
26034         this.el.select('a', true).first().on('click', this.onClick, this);
26035         
26036     },
26037     
26038     onClick : function(e)
26039     {
26040         if(this.preventDefault){
26041             e.preventDefault();
26042         }
26043         
26044         this.fireEvent("click", this, e);
26045     },
26046     
26047     onMouseOver : function(e)
26048     {
26049         if(this.submenu && this.pos == 'left'){
26050             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26051         }
26052         
26053         this.fireEvent("mouseover", this, e);
26054     },
26055     
26056     onMouseOut : function(e)
26057     {
26058         this.fireEvent("mouseout", this, e);
26059     }
26060 });
26061
26062  
26063
26064  /*
26065  * - LGPL
26066  *
26067  * menu separator
26068  * 
26069  */
26070 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26071
26072 /**
26073  * @class Roo.bootstrap.menu.Separator
26074  * @extends Roo.bootstrap.Component
26075  * Bootstrap Separator class
26076  * 
26077  * @constructor
26078  * Create a new Separator
26079  * @param {Object} config The config object
26080  */
26081
26082
26083 Roo.bootstrap.menu.Separator = function(config){
26084     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26085 };
26086
26087 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26088     
26089     getAutoCreate : function(){
26090         var cfg = {
26091             tag : 'li',
26092             cls: 'divider'
26093         };
26094         
26095         return cfg;
26096     }
26097    
26098 });
26099
26100  
26101
26102  /*
26103  * - LGPL
26104  *
26105  * Tooltip
26106  * 
26107  */
26108
26109 /**
26110  * @class Roo.bootstrap.Tooltip
26111  * Bootstrap Tooltip class
26112  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26113  * to determine which dom element triggers the tooltip.
26114  * 
26115  * It needs to add support for additional attributes like tooltip-position
26116  * 
26117  * @constructor
26118  * Create a new Toolti
26119  * @param {Object} config The config object
26120  */
26121
26122 Roo.bootstrap.Tooltip = function(config){
26123     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26124     
26125     this.alignment = Roo.bootstrap.Tooltip.alignment;
26126     
26127     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26128         this.alignment = config.alignment;
26129     }
26130     
26131 };
26132
26133 Roo.apply(Roo.bootstrap.Tooltip, {
26134     /**
26135      * @function init initialize tooltip monitoring.
26136      * @static
26137      */
26138     currentEl : false,
26139     currentTip : false,
26140     currentRegion : false,
26141     
26142     //  init : delay?
26143     
26144     init : function()
26145     {
26146         Roo.get(document).on('mouseover', this.enter ,this);
26147         Roo.get(document).on('mouseout', this.leave, this);
26148          
26149         
26150         this.currentTip = new Roo.bootstrap.Tooltip();
26151     },
26152     
26153     enter : function(ev)
26154     {
26155         var dom = ev.getTarget();
26156         
26157         //Roo.log(['enter',dom]);
26158         var el = Roo.fly(dom);
26159         if (this.currentEl) {
26160             //Roo.log(dom);
26161             //Roo.log(this.currentEl);
26162             //Roo.log(this.currentEl.contains(dom));
26163             if (this.currentEl == el) {
26164                 return;
26165             }
26166             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26167                 return;
26168             }
26169
26170         }
26171         
26172         if (this.currentTip.el) {
26173             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26174         }    
26175         //Roo.log(ev);
26176         
26177         if(!el || el.dom == document){
26178             return;
26179         }
26180         
26181         var bindEl = el;
26182         
26183         // you can not look for children, as if el is the body.. then everythign is the child..
26184         if (!el.attr('tooltip')) { //
26185             if (!el.select("[tooltip]").elements.length) {
26186                 return;
26187             }
26188             // is the mouse over this child...?
26189             bindEl = el.select("[tooltip]").first();
26190             var xy = ev.getXY();
26191             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26192                 //Roo.log("not in region.");
26193                 return;
26194             }
26195             //Roo.log("child element over..");
26196             
26197         }
26198         this.currentEl = bindEl;
26199         this.currentTip.bind(bindEl);
26200         this.currentRegion = Roo.lib.Region.getRegion(dom);
26201         this.currentTip.enter();
26202         
26203     },
26204     leave : function(ev)
26205     {
26206         var dom = ev.getTarget();
26207         //Roo.log(['leave',dom]);
26208         if (!this.currentEl) {
26209             return;
26210         }
26211         
26212         
26213         if (dom != this.currentEl.dom) {
26214             return;
26215         }
26216         var xy = ev.getXY();
26217         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26218             return;
26219         }
26220         // only activate leave if mouse cursor is outside... bounding box..
26221         
26222         
26223         
26224         
26225         if (this.currentTip) {
26226             this.currentTip.leave();
26227         }
26228         //Roo.log('clear currentEl');
26229         this.currentEl = false;
26230         
26231         
26232     },
26233     alignment : {
26234         'left' : ['r-l', [-2,0], 'right'],
26235         'right' : ['l-r', [2,0], 'left'],
26236         'bottom' : ['t-b', [0,2], 'top'],
26237         'top' : [ 'b-t', [0,-2], 'bottom']
26238     }
26239     
26240 });
26241
26242
26243 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26244     
26245     
26246     bindEl : false,
26247     
26248     delay : null, // can be { show : 300 , hide: 500}
26249     
26250     timeout : null,
26251     
26252     hoverState : null, //???
26253     
26254     placement : 'bottom', 
26255     
26256     alignment : false,
26257     
26258     getAutoCreate : function(){
26259     
26260         var cfg = {
26261            cls : 'tooltip',
26262            role : 'tooltip',
26263            cn : [
26264                 {
26265                     cls : 'tooltip-arrow'
26266                 },
26267                 {
26268                     cls : 'tooltip-inner'
26269                 }
26270            ]
26271         };
26272         
26273         return cfg;
26274     },
26275     bind : function(el)
26276     {
26277         this.bindEl = el;
26278     },
26279       
26280     
26281     enter : function () {
26282        
26283         if (this.timeout != null) {
26284             clearTimeout(this.timeout);
26285         }
26286         
26287         this.hoverState = 'in';
26288          //Roo.log("enter - show");
26289         if (!this.delay || !this.delay.show) {
26290             this.show();
26291             return;
26292         }
26293         var _t = this;
26294         this.timeout = setTimeout(function () {
26295             if (_t.hoverState == 'in') {
26296                 _t.show();
26297             }
26298         }, this.delay.show);
26299     },
26300     leave : function()
26301     {
26302         clearTimeout(this.timeout);
26303     
26304         this.hoverState = 'out';
26305          if (!this.delay || !this.delay.hide) {
26306             this.hide();
26307             return;
26308         }
26309        
26310         var _t = this;
26311         this.timeout = setTimeout(function () {
26312             //Roo.log("leave - timeout");
26313             
26314             if (_t.hoverState == 'out') {
26315                 _t.hide();
26316                 Roo.bootstrap.Tooltip.currentEl = false;
26317             }
26318         }, delay);
26319     },
26320     
26321     show : function (msg)
26322     {
26323         if (!this.el) {
26324             this.render(document.body);
26325         }
26326         // set content.
26327         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26328         
26329         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26330         
26331         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26332         
26333         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26334         
26335         var placement = typeof this.placement == 'function' ?
26336             this.placement.call(this, this.el, on_el) :
26337             this.placement;
26338             
26339         var autoToken = /\s?auto?\s?/i;
26340         var autoPlace = autoToken.test(placement);
26341         if (autoPlace) {
26342             placement = placement.replace(autoToken, '') || 'top';
26343         }
26344         
26345         //this.el.detach()
26346         //this.el.setXY([0,0]);
26347         this.el.show();
26348         //this.el.dom.style.display='block';
26349         
26350         //this.el.appendTo(on_el);
26351         
26352         var p = this.getPosition();
26353         var box = this.el.getBox();
26354         
26355         if (autoPlace) {
26356             // fixme..
26357         }
26358         
26359         var align = this.alignment[placement];
26360         
26361         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26362         
26363         if(placement == 'top' || placement == 'bottom'){
26364             if(xy[0] < 0){
26365                 placement = 'right';
26366             }
26367             
26368             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26369                 placement = 'left';
26370             }
26371             
26372             var scroll = Roo.select('body', true).first().getScroll();
26373             
26374             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26375                 placement = 'top';
26376             }
26377             
26378             align = this.alignment[placement];
26379         }
26380         
26381         this.el.alignTo(this.bindEl, align[0],align[1]);
26382         //var arrow = this.el.select('.arrow',true).first();
26383         //arrow.set(align[2], 
26384         
26385         this.el.addClass(placement);
26386         
26387         this.el.addClass('in fade');
26388         
26389         this.hoverState = null;
26390         
26391         if (this.el.hasClass('fade')) {
26392             // fade it?
26393         }
26394         
26395     },
26396     hide : function()
26397     {
26398          
26399         if (!this.el) {
26400             return;
26401         }
26402         //this.el.setXY([0,0]);
26403         this.el.removeClass('in');
26404         //this.el.hide();
26405         
26406     }
26407     
26408 });
26409  
26410
26411  /*
26412  * - LGPL
26413  *
26414  * Location Picker
26415  * 
26416  */
26417
26418 /**
26419  * @class Roo.bootstrap.LocationPicker
26420  * @extends Roo.bootstrap.Component
26421  * Bootstrap LocationPicker class
26422  * @cfg {Number} latitude Position when init default 0
26423  * @cfg {Number} longitude Position when init default 0
26424  * @cfg {Number} zoom default 15
26425  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26426  * @cfg {Boolean} mapTypeControl default false
26427  * @cfg {Boolean} disableDoubleClickZoom default false
26428  * @cfg {Boolean} scrollwheel default true
26429  * @cfg {Boolean} streetViewControl default false
26430  * @cfg {Number} radius default 0
26431  * @cfg {String} locationName
26432  * @cfg {Boolean} draggable default true
26433  * @cfg {Boolean} enableAutocomplete default false
26434  * @cfg {Boolean} enableReverseGeocode default true
26435  * @cfg {String} markerTitle
26436  * 
26437  * @constructor
26438  * Create a new LocationPicker
26439  * @param {Object} config The config object
26440  */
26441
26442
26443 Roo.bootstrap.LocationPicker = function(config){
26444     
26445     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26446     
26447     this.addEvents({
26448         /**
26449          * @event initial
26450          * Fires when the picker initialized.
26451          * @param {Roo.bootstrap.LocationPicker} this
26452          * @param {Google Location} location
26453          */
26454         initial : true,
26455         /**
26456          * @event positionchanged
26457          * Fires when the picker position changed.
26458          * @param {Roo.bootstrap.LocationPicker} this
26459          * @param {Google Location} location
26460          */
26461         positionchanged : true,
26462         /**
26463          * @event resize
26464          * Fires when the map resize.
26465          * @param {Roo.bootstrap.LocationPicker} this
26466          */
26467         resize : true,
26468         /**
26469          * @event show
26470          * Fires when the map show.
26471          * @param {Roo.bootstrap.LocationPicker} this
26472          */
26473         show : true,
26474         /**
26475          * @event hide
26476          * Fires when the map hide.
26477          * @param {Roo.bootstrap.LocationPicker} this
26478          */
26479         hide : true,
26480         /**
26481          * @event mapClick
26482          * Fires when click the map.
26483          * @param {Roo.bootstrap.LocationPicker} this
26484          * @param {Map event} e
26485          */
26486         mapClick : true,
26487         /**
26488          * @event mapRightClick
26489          * Fires when right click the map.
26490          * @param {Roo.bootstrap.LocationPicker} this
26491          * @param {Map event} e
26492          */
26493         mapRightClick : true,
26494         /**
26495          * @event markerClick
26496          * Fires when click the marker.
26497          * @param {Roo.bootstrap.LocationPicker} this
26498          * @param {Map event} e
26499          */
26500         markerClick : true,
26501         /**
26502          * @event markerRightClick
26503          * Fires when right click the marker.
26504          * @param {Roo.bootstrap.LocationPicker} this
26505          * @param {Map event} e
26506          */
26507         markerRightClick : true,
26508         /**
26509          * @event OverlayViewDraw
26510          * Fires when OverlayView Draw
26511          * @param {Roo.bootstrap.LocationPicker} this
26512          */
26513         OverlayViewDraw : true,
26514         /**
26515          * @event OverlayViewOnAdd
26516          * Fires when OverlayView Draw
26517          * @param {Roo.bootstrap.LocationPicker} this
26518          */
26519         OverlayViewOnAdd : true,
26520         /**
26521          * @event OverlayViewOnRemove
26522          * Fires when OverlayView Draw
26523          * @param {Roo.bootstrap.LocationPicker} this
26524          */
26525         OverlayViewOnRemove : true,
26526         /**
26527          * @event OverlayViewShow
26528          * Fires when OverlayView Draw
26529          * @param {Roo.bootstrap.LocationPicker} this
26530          * @param {Pixel} cpx
26531          */
26532         OverlayViewShow : true,
26533         /**
26534          * @event OverlayViewHide
26535          * Fires when OverlayView Draw
26536          * @param {Roo.bootstrap.LocationPicker} this
26537          */
26538         OverlayViewHide : true,
26539         /**
26540          * @event loadexception
26541          * Fires when load google lib failed.
26542          * @param {Roo.bootstrap.LocationPicker} this
26543          */
26544         loadexception : true
26545     });
26546         
26547 };
26548
26549 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26550     
26551     gMapContext: false,
26552     
26553     latitude: 0,
26554     longitude: 0,
26555     zoom: 15,
26556     mapTypeId: false,
26557     mapTypeControl: false,
26558     disableDoubleClickZoom: false,
26559     scrollwheel: true,
26560     streetViewControl: false,
26561     radius: 0,
26562     locationName: '',
26563     draggable: true,
26564     enableAutocomplete: false,
26565     enableReverseGeocode: true,
26566     markerTitle: '',
26567     
26568     getAutoCreate: function()
26569     {
26570
26571         var cfg = {
26572             tag: 'div',
26573             cls: 'roo-location-picker'
26574         };
26575         
26576         return cfg
26577     },
26578     
26579     initEvents: function(ct, position)
26580     {       
26581         if(!this.el.getWidth() || this.isApplied()){
26582             return;
26583         }
26584         
26585         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26586         
26587         this.initial();
26588     },
26589     
26590     initial: function()
26591     {
26592         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26593             this.fireEvent('loadexception', this);
26594             return;
26595         }
26596         
26597         if(!this.mapTypeId){
26598             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26599         }
26600         
26601         this.gMapContext = this.GMapContext();
26602         
26603         this.initOverlayView();
26604         
26605         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26606         
26607         var _this = this;
26608                 
26609         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26610             _this.setPosition(_this.gMapContext.marker.position);
26611         });
26612         
26613         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26614             _this.fireEvent('mapClick', this, event);
26615             
26616         });
26617
26618         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26619             _this.fireEvent('mapRightClick', this, event);
26620             
26621         });
26622         
26623         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26624             _this.fireEvent('markerClick', this, event);
26625             
26626         });
26627
26628         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26629             _this.fireEvent('markerRightClick', this, event);
26630             
26631         });
26632         
26633         this.setPosition(this.gMapContext.location);
26634         
26635         this.fireEvent('initial', this, this.gMapContext.location);
26636     },
26637     
26638     initOverlayView: function()
26639     {
26640         var _this = this;
26641         
26642         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26643             
26644             draw: function()
26645             {
26646                 _this.fireEvent('OverlayViewDraw', _this);
26647             },
26648             
26649             onAdd: function()
26650             {
26651                 _this.fireEvent('OverlayViewOnAdd', _this);
26652             },
26653             
26654             onRemove: function()
26655             {
26656                 _this.fireEvent('OverlayViewOnRemove', _this);
26657             },
26658             
26659             show: function(cpx)
26660             {
26661                 _this.fireEvent('OverlayViewShow', _this, cpx);
26662             },
26663             
26664             hide: function()
26665             {
26666                 _this.fireEvent('OverlayViewHide', _this);
26667             }
26668             
26669         });
26670     },
26671     
26672     fromLatLngToContainerPixel: function(event)
26673     {
26674         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26675     },
26676     
26677     isApplied: function() 
26678     {
26679         return this.getGmapContext() == false ? false : true;
26680     },
26681     
26682     getGmapContext: function() 
26683     {
26684         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26685     },
26686     
26687     GMapContext: function() 
26688     {
26689         var position = new google.maps.LatLng(this.latitude, this.longitude);
26690         
26691         var _map = new google.maps.Map(this.el.dom, {
26692             center: position,
26693             zoom: this.zoom,
26694             mapTypeId: this.mapTypeId,
26695             mapTypeControl: this.mapTypeControl,
26696             disableDoubleClickZoom: this.disableDoubleClickZoom,
26697             scrollwheel: this.scrollwheel,
26698             streetViewControl: this.streetViewControl,
26699             locationName: this.locationName,
26700             draggable: this.draggable,
26701             enableAutocomplete: this.enableAutocomplete,
26702             enableReverseGeocode: this.enableReverseGeocode
26703         });
26704         
26705         var _marker = new google.maps.Marker({
26706             position: position,
26707             map: _map,
26708             title: this.markerTitle,
26709             draggable: this.draggable
26710         });
26711         
26712         return {
26713             map: _map,
26714             marker: _marker,
26715             circle: null,
26716             location: position,
26717             radius: this.radius,
26718             locationName: this.locationName,
26719             addressComponents: {
26720                 formatted_address: null,
26721                 addressLine1: null,
26722                 addressLine2: null,
26723                 streetName: null,
26724                 streetNumber: null,
26725                 city: null,
26726                 district: null,
26727                 state: null,
26728                 stateOrProvince: null
26729             },
26730             settings: this,
26731             domContainer: this.el.dom,
26732             geodecoder: new google.maps.Geocoder()
26733         };
26734     },
26735     
26736     drawCircle: function(center, radius, options) 
26737     {
26738         if (this.gMapContext.circle != null) {
26739             this.gMapContext.circle.setMap(null);
26740         }
26741         if (radius > 0) {
26742             radius *= 1;
26743             options = Roo.apply({}, options, {
26744                 strokeColor: "#0000FF",
26745                 strokeOpacity: .35,
26746                 strokeWeight: 2,
26747                 fillColor: "#0000FF",
26748                 fillOpacity: .2
26749             });
26750             
26751             options.map = this.gMapContext.map;
26752             options.radius = radius;
26753             options.center = center;
26754             this.gMapContext.circle = new google.maps.Circle(options);
26755             return this.gMapContext.circle;
26756         }
26757         
26758         return null;
26759     },
26760     
26761     setPosition: function(location) 
26762     {
26763         this.gMapContext.location = location;
26764         this.gMapContext.marker.setPosition(location);
26765         this.gMapContext.map.panTo(location);
26766         this.drawCircle(location, this.gMapContext.radius, {});
26767         
26768         var _this = this;
26769         
26770         if (this.gMapContext.settings.enableReverseGeocode) {
26771             this.gMapContext.geodecoder.geocode({
26772                 latLng: this.gMapContext.location
26773             }, function(results, status) {
26774                 
26775                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26776                     _this.gMapContext.locationName = results[0].formatted_address;
26777                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26778                     
26779                     _this.fireEvent('positionchanged', this, location);
26780                 }
26781             });
26782             
26783             return;
26784         }
26785         
26786         this.fireEvent('positionchanged', this, location);
26787     },
26788     
26789     resize: function()
26790     {
26791         google.maps.event.trigger(this.gMapContext.map, "resize");
26792         
26793         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26794         
26795         this.fireEvent('resize', this);
26796     },
26797     
26798     setPositionByLatLng: function(latitude, longitude)
26799     {
26800         this.setPosition(new google.maps.LatLng(latitude, longitude));
26801     },
26802     
26803     getCurrentPosition: function() 
26804     {
26805         return {
26806             latitude: this.gMapContext.location.lat(),
26807             longitude: this.gMapContext.location.lng()
26808         };
26809     },
26810     
26811     getAddressName: function() 
26812     {
26813         return this.gMapContext.locationName;
26814     },
26815     
26816     getAddressComponents: function() 
26817     {
26818         return this.gMapContext.addressComponents;
26819     },
26820     
26821     address_component_from_google_geocode: function(address_components) 
26822     {
26823         var result = {};
26824         
26825         for (var i = 0; i < address_components.length; i++) {
26826             var component = address_components[i];
26827             if (component.types.indexOf("postal_code") >= 0) {
26828                 result.postalCode = component.short_name;
26829             } else if (component.types.indexOf("street_number") >= 0) {
26830                 result.streetNumber = component.short_name;
26831             } else if (component.types.indexOf("route") >= 0) {
26832                 result.streetName = component.short_name;
26833             } else if (component.types.indexOf("neighborhood") >= 0) {
26834                 result.city = component.short_name;
26835             } else if (component.types.indexOf("locality") >= 0) {
26836                 result.city = component.short_name;
26837             } else if (component.types.indexOf("sublocality") >= 0) {
26838                 result.district = component.short_name;
26839             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26840                 result.stateOrProvince = component.short_name;
26841             } else if (component.types.indexOf("country") >= 0) {
26842                 result.country = component.short_name;
26843             }
26844         }
26845         
26846         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26847         result.addressLine2 = "";
26848         return result;
26849     },
26850     
26851     setZoomLevel: function(zoom)
26852     {
26853         this.gMapContext.map.setZoom(zoom);
26854     },
26855     
26856     show: function()
26857     {
26858         if(!this.el){
26859             return;
26860         }
26861         
26862         this.el.show();
26863         
26864         this.resize();
26865         
26866         this.fireEvent('show', this);
26867     },
26868     
26869     hide: function()
26870     {
26871         if(!this.el){
26872             return;
26873         }
26874         
26875         this.el.hide();
26876         
26877         this.fireEvent('hide', this);
26878     }
26879     
26880 });
26881
26882 Roo.apply(Roo.bootstrap.LocationPicker, {
26883     
26884     OverlayView : function(map, options)
26885     {
26886         options = options || {};
26887         
26888         this.setMap(map);
26889     }
26890     
26891     
26892 });/*
26893  * - LGPL
26894  *
26895  * Alert
26896  * 
26897  */
26898
26899 /**
26900  * @class Roo.bootstrap.Alert
26901  * @extends Roo.bootstrap.Component
26902  * Bootstrap Alert class
26903  * @cfg {String} title The title of alert
26904  * @cfg {String} html The content of alert
26905  * @cfg {String} weight (  success | info | warning | danger )
26906  * @cfg {String} faicon font-awesomeicon
26907  * 
26908  * @constructor
26909  * Create a new alert
26910  * @param {Object} config The config object
26911  */
26912
26913
26914 Roo.bootstrap.Alert = function(config){
26915     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26916     
26917 };
26918
26919 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26920     
26921     title: '',
26922     html: '',
26923     weight: false,
26924     faicon: false,
26925     
26926     getAutoCreate : function()
26927     {
26928         
26929         var cfg = {
26930             tag : 'div',
26931             cls : 'alert',
26932             cn : [
26933                 {
26934                     tag : 'i',
26935                     cls : 'roo-alert-icon'
26936                     
26937                 },
26938                 {
26939                     tag : 'b',
26940                     cls : 'roo-alert-title',
26941                     html : this.title
26942                 },
26943                 {
26944                     tag : 'span',
26945                     cls : 'roo-alert-text',
26946                     html : this.html
26947                 }
26948             ]
26949         };
26950         
26951         if(this.faicon){
26952             cfg.cn[0].cls += ' fa ' + this.faicon;
26953         }
26954         
26955         if(this.weight){
26956             cfg.cls += ' alert-' + this.weight;
26957         }
26958         
26959         return cfg;
26960     },
26961     
26962     initEvents: function() 
26963     {
26964         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26965     },
26966     
26967     setTitle : function(str)
26968     {
26969         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26970     },
26971     
26972     setText : function(str)
26973     {
26974         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26975     },
26976     
26977     setWeight : function(weight)
26978     {
26979         if(this.weight){
26980             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26981         }
26982         
26983         this.weight = weight;
26984         
26985         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26986     },
26987     
26988     setIcon : function(icon)
26989     {
26990         if(this.faicon){
26991             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26992         }
26993         
26994         this.faicon = icon;
26995         
26996         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26997     },
26998     
26999     hide: function() 
27000     {
27001         this.el.hide();   
27002     },
27003     
27004     show: function() 
27005     {  
27006         this.el.show();   
27007     }
27008     
27009 });
27010
27011  
27012 /*
27013 * Licence: LGPL
27014 */
27015
27016 /**
27017  * @class Roo.bootstrap.UploadCropbox
27018  * @extends Roo.bootstrap.Component
27019  * Bootstrap UploadCropbox class
27020  * @cfg {String} emptyText show when image has been loaded
27021  * @cfg {String} rotateNotify show when image too small to rotate
27022  * @cfg {Number} errorTimeout default 3000
27023  * @cfg {Number} minWidth default 300
27024  * @cfg {Number} minHeight default 300
27025  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27026  * @cfg {Boolean} isDocument (true|false) default false
27027  * @cfg {String} url action url
27028  * @cfg {String} paramName default 'imageUpload'
27029  * @cfg {String} method default POST
27030  * @cfg {Boolean} loadMask (true|false) default true
27031  * @cfg {Boolean} loadingText default 'Loading...'
27032  * 
27033  * @constructor
27034  * Create a new UploadCropbox
27035  * @param {Object} config The config object
27036  */
27037
27038 Roo.bootstrap.UploadCropbox = function(config){
27039     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27040     
27041     this.addEvents({
27042         /**
27043          * @event beforeselectfile
27044          * Fire before select file
27045          * @param {Roo.bootstrap.UploadCropbox} this
27046          */
27047         "beforeselectfile" : true,
27048         /**
27049          * @event initial
27050          * Fire after initEvent
27051          * @param {Roo.bootstrap.UploadCropbox} this
27052          */
27053         "initial" : true,
27054         /**
27055          * @event crop
27056          * Fire after initEvent
27057          * @param {Roo.bootstrap.UploadCropbox} this
27058          * @param {String} data
27059          */
27060         "crop" : true,
27061         /**
27062          * @event prepare
27063          * Fire when preparing the file data
27064          * @param {Roo.bootstrap.UploadCropbox} this
27065          * @param {Object} file
27066          */
27067         "prepare" : true,
27068         /**
27069          * @event exception
27070          * Fire when get exception
27071          * @param {Roo.bootstrap.UploadCropbox} this
27072          * @param {XMLHttpRequest} xhr
27073          */
27074         "exception" : true,
27075         /**
27076          * @event beforeloadcanvas
27077          * Fire before load the canvas
27078          * @param {Roo.bootstrap.UploadCropbox} this
27079          * @param {String} src
27080          */
27081         "beforeloadcanvas" : true,
27082         /**
27083          * @event trash
27084          * Fire when trash image
27085          * @param {Roo.bootstrap.UploadCropbox} this
27086          */
27087         "trash" : true,
27088         /**
27089          * @event download
27090          * Fire when download the image
27091          * @param {Roo.bootstrap.UploadCropbox} this
27092          */
27093         "download" : true,
27094         /**
27095          * @event footerbuttonclick
27096          * Fire when footerbuttonclick
27097          * @param {Roo.bootstrap.UploadCropbox} this
27098          * @param {String} type
27099          */
27100         "footerbuttonclick" : true,
27101         /**
27102          * @event resize
27103          * Fire when resize
27104          * @param {Roo.bootstrap.UploadCropbox} this
27105          */
27106         "resize" : true,
27107         /**
27108          * @event rotate
27109          * Fire when rotate the image
27110          * @param {Roo.bootstrap.UploadCropbox} this
27111          * @param {String} pos
27112          */
27113         "rotate" : true,
27114         /**
27115          * @event inspect
27116          * Fire when inspect the file
27117          * @param {Roo.bootstrap.UploadCropbox} this
27118          * @param {Object} file
27119          */
27120         "inspect" : true,
27121         /**
27122          * @event upload
27123          * Fire when xhr upload the file
27124          * @param {Roo.bootstrap.UploadCropbox} this
27125          * @param {Object} data
27126          */
27127         "upload" : true,
27128         /**
27129          * @event arrange
27130          * Fire when arrange the file data
27131          * @param {Roo.bootstrap.UploadCropbox} this
27132          * @param {Object} formData
27133          */
27134         "arrange" : true
27135     });
27136     
27137     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27138 };
27139
27140 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27141     
27142     emptyText : 'Click to upload image',
27143     rotateNotify : 'Image is too small to rotate',
27144     errorTimeout : 3000,
27145     scale : 0,
27146     baseScale : 1,
27147     rotate : 0,
27148     dragable : false,
27149     pinching : false,
27150     mouseX : 0,
27151     mouseY : 0,
27152     cropData : false,
27153     minWidth : 300,
27154     minHeight : 300,
27155     file : false,
27156     exif : {},
27157     baseRotate : 1,
27158     cropType : 'image/jpeg',
27159     buttons : false,
27160     canvasLoaded : false,
27161     isDocument : false,
27162     method : 'POST',
27163     paramName : 'imageUpload',
27164     loadMask : true,
27165     loadingText : 'Loading...',
27166     maskEl : false,
27167     
27168     getAutoCreate : function()
27169     {
27170         var cfg = {
27171             tag : 'div',
27172             cls : 'roo-upload-cropbox',
27173             cn : [
27174                 {
27175                     tag : 'input',
27176                     cls : 'roo-upload-cropbox-selector',
27177                     type : 'file'
27178                 },
27179                 {
27180                     tag : 'div',
27181                     cls : 'roo-upload-cropbox-body',
27182                     style : 'cursor:pointer',
27183                     cn : [
27184                         {
27185                             tag : 'div',
27186                             cls : 'roo-upload-cropbox-preview'
27187                         },
27188                         {
27189                             tag : 'div',
27190                             cls : 'roo-upload-cropbox-thumb'
27191                         },
27192                         {
27193                             tag : 'div',
27194                             cls : 'roo-upload-cropbox-empty-notify',
27195                             html : this.emptyText
27196                         },
27197                         {
27198                             tag : 'div',
27199                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27200                             html : this.rotateNotify
27201                         }
27202                     ]
27203                 },
27204                 {
27205                     tag : 'div',
27206                     cls : 'roo-upload-cropbox-footer',
27207                     cn : {
27208                         tag : 'div',
27209                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27210                         cn : []
27211                     }
27212                 }
27213             ]
27214         };
27215         
27216         return cfg;
27217     },
27218     
27219     onRender : function(ct, position)
27220     {
27221         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27222         
27223         if (this.buttons.length) {
27224             
27225             Roo.each(this.buttons, function(bb) {
27226                 
27227                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27228                 
27229                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27230                 
27231             }, this);
27232         }
27233         
27234         if(this.loadMask){
27235             this.maskEl = this.el;
27236         }
27237     },
27238     
27239     initEvents : function()
27240     {
27241         this.urlAPI = (window.createObjectURL && window) || 
27242                                 (window.URL && URL.revokeObjectURL && URL) || 
27243                                 (window.webkitURL && webkitURL);
27244                         
27245         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27246         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27247         
27248         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27249         this.selectorEl.hide();
27250         
27251         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27252         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27253         
27254         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27255         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27256         this.thumbEl.hide();
27257         
27258         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27259         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27260         
27261         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27262         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27263         this.errorEl.hide();
27264         
27265         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27266         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27267         this.footerEl.hide();
27268         
27269         this.setThumbBoxSize();
27270         
27271         this.bind();
27272         
27273         this.resize();
27274         
27275         this.fireEvent('initial', this);
27276     },
27277
27278     bind : function()
27279     {
27280         var _this = this;
27281         
27282         window.addEventListener("resize", function() { _this.resize(); } );
27283         
27284         this.bodyEl.on('click', this.beforeSelectFile, this);
27285         
27286         if(Roo.isTouch){
27287             this.bodyEl.on('touchstart', this.onTouchStart, this);
27288             this.bodyEl.on('touchmove', this.onTouchMove, this);
27289             this.bodyEl.on('touchend', this.onTouchEnd, this);
27290         }
27291         
27292         if(!Roo.isTouch){
27293             this.bodyEl.on('mousedown', this.onMouseDown, this);
27294             this.bodyEl.on('mousemove', this.onMouseMove, this);
27295             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27296             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27297             Roo.get(document).on('mouseup', this.onMouseUp, this);
27298         }
27299         
27300         this.selectorEl.on('change', this.onFileSelected, this);
27301     },
27302     
27303     reset : function()
27304     {    
27305         this.scale = 0;
27306         this.baseScale = 1;
27307         this.rotate = 0;
27308         this.baseRotate = 1;
27309         this.dragable = false;
27310         this.pinching = false;
27311         this.mouseX = 0;
27312         this.mouseY = 0;
27313         this.cropData = false;
27314         this.notifyEl.dom.innerHTML = this.emptyText;
27315         
27316         this.selectorEl.dom.value = '';
27317         
27318     },
27319     
27320     resize : function()
27321     {
27322         if(this.fireEvent('resize', this) != false){
27323             this.setThumbBoxPosition();
27324             this.setCanvasPosition();
27325         }
27326     },
27327     
27328     onFooterButtonClick : function(e, el, o, type)
27329     {
27330         switch (type) {
27331             case 'rotate-left' :
27332                 this.onRotateLeft(e);
27333                 break;
27334             case 'rotate-right' :
27335                 this.onRotateRight(e);
27336                 break;
27337             case 'picture' :
27338                 this.beforeSelectFile(e);
27339                 break;
27340             case 'trash' :
27341                 this.trash(e);
27342                 break;
27343             case 'crop' :
27344                 this.crop(e);
27345                 break;
27346             case 'download' :
27347                 this.download(e);
27348                 break;
27349             default :
27350                 break;
27351         }
27352         
27353         this.fireEvent('footerbuttonclick', this, type);
27354     },
27355     
27356     beforeSelectFile : function(e)
27357     {
27358         e.preventDefault();
27359         
27360         if(this.fireEvent('beforeselectfile', this) != false){
27361             this.selectorEl.dom.click();
27362         }
27363     },
27364     
27365     onFileSelected : function(e)
27366     {
27367         e.preventDefault();
27368         
27369         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27370             return;
27371         }
27372         
27373         var file = this.selectorEl.dom.files[0];
27374         
27375         if(this.fireEvent('inspect', this, file) != false){
27376             this.prepare(file);
27377         }
27378         
27379     },
27380     
27381     trash : function(e)
27382     {
27383         this.fireEvent('trash', this);
27384     },
27385     
27386     download : function(e)
27387     {
27388         this.fireEvent('download', this);
27389     },
27390     
27391     loadCanvas : function(src)
27392     {   
27393         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27394             
27395             this.reset();
27396             
27397             this.imageEl = document.createElement('img');
27398             
27399             var _this = this;
27400             
27401             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27402             
27403             this.imageEl.src = src;
27404         }
27405     },
27406     
27407     onLoadCanvas : function()
27408     {   
27409         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27410         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27411         
27412         this.bodyEl.un('click', this.beforeSelectFile, this);
27413         
27414         this.notifyEl.hide();
27415         this.thumbEl.show();
27416         this.footerEl.show();
27417         
27418         this.baseRotateLevel();
27419         
27420         if(this.isDocument){
27421             this.setThumbBoxSize();
27422         }
27423         
27424         this.setThumbBoxPosition();
27425         
27426         this.baseScaleLevel();
27427         
27428         this.draw();
27429         
27430         this.resize();
27431         
27432         this.canvasLoaded = true;
27433         
27434         if(this.loadMask){
27435             this.maskEl.unmask();
27436         }
27437         
27438     },
27439     
27440     setCanvasPosition : function()
27441     {   
27442         if(!this.canvasEl){
27443             return;
27444         }
27445         
27446         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27447         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27448         
27449         this.previewEl.setLeft(pw);
27450         this.previewEl.setTop(ph);
27451         
27452     },
27453     
27454     onMouseDown : function(e)
27455     {   
27456         e.stopEvent();
27457         
27458         this.dragable = true;
27459         this.pinching = false;
27460         
27461         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27462             this.dragable = false;
27463             return;
27464         }
27465         
27466         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27467         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27468         
27469     },
27470     
27471     onMouseMove : function(e)
27472     {   
27473         e.stopEvent();
27474         
27475         if(!this.canvasLoaded){
27476             return;
27477         }
27478         
27479         if (!this.dragable){
27480             return;
27481         }
27482         
27483         var minX = Math.ceil(this.thumbEl.getLeft(true));
27484         var minY = Math.ceil(this.thumbEl.getTop(true));
27485         
27486         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27487         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27488         
27489         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27490         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27491         
27492         x = x - this.mouseX;
27493         y = y - this.mouseY;
27494         
27495         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27496         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27497         
27498         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27499         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27500         
27501         this.previewEl.setLeft(bgX);
27502         this.previewEl.setTop(bgY);
27503         
27504         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27505         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27506     },
27507     
27508     onMouseUp : function(e)
27509     {   
27510         e.stopEvent();
27511         
27512         this.dragable = false;
27513     },
27514     
27515     onMouseWheel : function(e)
27516     {   
27517         e.stopEvent();
27518         
27519         this.startScale = this.scale;
27520         
27521         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27522         
27523         if(!this.zoomable()){
27524             this.scale = this.startScale;
27525             return;
27526         }
27527         
27528         this.draw();
27529         
27530         return;
27531     },
27532     
27533     zoomable : function()
27534     {
27535         var minScale = this.thumbEl.getWidth() / this.minWidth;
27536         
27537         if(this.minWidth < this.minHeight){
27538             minScale = this.thumbEl.getHeight() / this.minHeight;
27539         }
27540         
27541         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27542         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27543         
27544         if(
27545                 this.isDocument &&
27546                 (this.rotate == 0 || this.rotate == 180) && 
27547                 (
27548                     width > this.imageEl.OriginWidth || 
27549                     height > this.imageEl.OriginHeight ||
27550                     (width < this.minWidth && height < this.minHeight)
27551                 )
27552         ){
27553             return false;
27554         }
27555         
27556         if(
27557                 this.isDocument &&
27558                 (this.rotate == 90 || this.rotate == 270) && 
27559                 (
27560                     width > this.imageEl.OriginWidth || 
27561                     height > this.imageEl.OriginHeight ||
27562                     (width < this.minHeight && height < this.minWidth)
27563                 )
27564         ){
27565             return false;
27566         }
27567         
27568         if(
27569                 !this.isDocument &&
27570                 (this.rotate == 0 || this.rotate == 180) && 
27571                 (
27572                     width < this.minWidth || 
27573                     width > this.imageEl.OriginWidth || 
27574                     height < this.minHeight || 
27575                     height > this.imageEl.OriginHeight
27576                 )
27577         ){
27578             return false;
27579         }
27580         
27581         if(
27582                 !this.isDocument &&
27583                 (this.rotate == 90 || this.rotate == 270) && 
27584                 (
27585                     width < this.minHeight || 
27586                     width > this.imageEl.OriginWidth || 
27587                     height < this.minWidth || 
27588                     height > this.imageEl.OriginHeight
27589                 )
27590         ){
27591             return false;
27592         }
27593         
27594         return true;
27595         
27596     },
27597     
27598     onRotateLeft : function(e)
27599     {   
27600         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27601             
27602             var minScale = this.thumbEl.getWidth() / this.minWidth;
27603             
27604             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27605             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27606             
27607             this.startScale = this.scale;
27608             
27609             while (this.getScaleLevel() < minScale){
27610             
27611                 this.scale = this.scale + 1;
27612                 
27613                 if(!this.zoomable()){
27614                     break;
27615                 }
27616                 
27617                 if(
27618                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27619                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27620                 ){
27621                     continue;
27622                 }
27623                 
27624                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27625
27626                 this.draw();
27627                 
27628                 return;
27629             }
27630             
27631             this.scale = this.startScale;
27632             
27633             this.onRotateFail();
27634             
27635             return false;
27636         }
27637         
27638         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27639
27640         if(this.isDocument){
27641             this.setThumbBoxSize();
27642             this.setThumbBoxPosition();
27643             this.setCanvasPosition();
27644         }
27645         
27646         this.draw();
27647         
27648         this.fireEvent('rotate', this, 'left');
27649         
27650     },
27651     
27652     onRotateRight : function(e)
27653     {
27654         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27655             
27656             var minScale = this.thumbEl.getWidth() / this.minWidth;
27657         
27658             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27659             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27660             
27661             this.startScale = this.scale;
27662             
27663             while (this.getScaleLevel() < minScale){
27664             
27665                 this.scale = this.scale + 1;
27666                 
27667                 if(!this.zoomable()){
27668                     break;
27669                 }
27670                 
27671                 if(
27672                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27673                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27674                 ){
27675                     continue;
27676                 }
27677                 
27678                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27679
27680                 this.draw();
27681                 
27682                 return;
27683             }
27684             
27685             this.scale = this.startScale;
27686             
27687             this.onRotateFail();
27688             
27689             return false;
27690         }
27691         
27692         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27693
27694         if(this.isDocument){
27695             this.setThumbBoxSize();
27696             this.setThumbBoxPosition();
27697             this.setCanvasPosition();
27698         }
27699         
27700         this.draw();
27701         
27702         this.fireEvent('rotate', this, 'right');
27703     },
27704     
27705     onRotateFail : function()
27706     {
27707         this.errorEl.show(true);
27708         
27709         var _this = this;
27710         
27711         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27712     },
27713     
27714     draw : function()
27715     {
27716         this.previewEl.dom.innerHTML = '';
27717         
27718         var canvasEl = document.createElement("canvas");
27719         
27720         var contextEl = canvasEl.getContext("2d");
27721         
27722         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27723         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27724         var center = this.imageEl.OriginWidth / 2;
27725         
27726         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27727             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27728             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27729             center = this.imageEl.OriginHeight / 2;
27730         }
27731         
27732         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27733         
27734         contextEl.translate(center, center);
27735         contextEl.rotate(this.rotate * Math.PI / 180);
27736
27737         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27738         
27739         this.canvasEl = document.createElement("canvas");
27740         
27741         this.contextEl = this.canvasEl.getContext("2d");
27742         
27743         switch (this.rotate) {
27744             case 0 :
27745                 
27746                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27747                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27748                 
27749                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27750                 
27751                 break;
27752             case 90 : 
27753                 
27754                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27755                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27756                 
27757                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27758                     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);
27759                     break;
27760                 }
27761                 
27762                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27763                 
27764                 break;
27765             case 180 :
27766                 
27767                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27768                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27769                 
27770                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27771                     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);
27772                     break;
27773                 }
27774                 
27775                 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);
27776                 
27777                 break;
27778             case 270 :
27779                 
27780                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27781                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27782         
27783                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27784                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27785                     break;
27786                 }
27787                 
27788                 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);
27789                 
27790                 break;
27791             default : 
27792                 break;
27793         }
27794         
27795         this.previewEl.appendChild(this.canvasEl);
27796         
27797         this.setCanvasPosition();
27798     },
27799     
27800     crop : function()
27801     {
27802         if(!this.canvasLoaded){
27803             return;
27804         }
27805         
27806         var imageCanvas = document.createElement("canvas");
27807         
27808         var imageContext = imageCanvas.getContext("2d");
27809         
27810         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27811         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27812         
27813         var center = imageCanvas.width / 2;
27814         
27815         imageContext.translate(center, center);
27816         
27817         imageContext.rotate(this.rotate * Math.PI / 180);
27818         
27819         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27820         
27821         var canvas = document.createElement("canvas");
27822         
27823         var context = canvas.getContext("2d");
27824                 
27825         canvas.width = this.minWidth;
27826         canvas.height = this.minHeight;
27827
27828         switch (this.rotate) {
27829             case 0 :
27830                 
27831                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27832                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27833                 
27834                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27835                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27836                 
27837                 var targetWidth = this.minWidth - 2 * x;
27838                 var targetHeight = this.minHeight - 2 * y;
27839                 
27840                 var scale = 1;
27841                 
27842                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27843                     scale = targetWidth / width;
27844                 }
27845                 
27846                 if(x > 0 && y == 0){
27847                     scale = targetHeight / height;
27848                 }
27849                 
27850                 if(x > 0 && y > 0){
27851                     scale = targetWidth / width;
27852                     
27853                     if(width < height){
27854                         scale = targetHeight / height;
27855                     }
27856                 }
27857                 
27858                 context.scale(scale, scale);
27859                 
27860                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27861                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27862
27863                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27864                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27865
27866                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27867                 
27868                 break;
27869             case 90 : 
27870                 
27871                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27872                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27873                 
27874                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27875                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27876                 
27877                 var targetWidth = this.minWidth - 2 * x;
27878                 var targetHeight = this.minHeight - 2 * y;
27879                 
27880                 var scale = 1;
27881                 
27882                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27883                     scale = targetWidth / width;
27884                 }
27885                 
27886                 if(x > 0 && y == 0){
27887                     scale = targetHeight / height;
27888                 }
27889                 
27890                 if(x > 0 && y > 0){
27891                     scale = targetWidth / width;
27892                     
27893                     if(width < height){
27894                         scale = targetHeight / height;
27895                     }
27896                 }
27897                 
27898                 context.scale(scale, scale);
27899                 
27900                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27901                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27902
27903                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27904                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27905                 
27906                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27907                 
27908                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27909                 
27910                 break;
27911             case 180 :
27912                 
27913                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27914                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27915                 
27916                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27917                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27918                 
27919                 var targetWidth = this.minWidth - 2 * x;
27920                 var targetHeight = this.minHeight - 2 * y;
27921                 
27922                 var scale = 1;
27923                 
27924                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27925                     scale = targetWidth / width;
27926                 }
27927                 
27928                 if(x > 0 && y == 0){
27929                     scale = targetHeight / height;
27930                 }
27931                 
27932                 if(x > 0 && y > 0){
27933                     scale = targetWidth / width;
27934                     
27935                     if(width < height){
27936                         scale = targetHeight / height;
27937                     }
27938                 }
27939                 
27940                 context.scale(scale, scale);
27941                 
27942                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27943                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27944
27945                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27946                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27947
27948                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27949                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27950                 
27951                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27952                 
27953                 break;
27954             case 270 :
27955                 
27956                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27957                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27958                 
27959                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27960                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27961                 
27962                 var targetWidth = this.minWidth - 2 * x;
27963                 var targetHeight = this.minHeight - 2 * y;
27964                 
27965                 var scale = 1;
27966                 
27967                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27968                     scale = targetWidth / width;
27969                 }
27970                 
27971                 if(x > 0 && y == 0){
27972                     scale = targetHeight / height;
27973                 }
27974                 
27975                 if(x > 0 && y > 0){
27976                     scale = targetWidth / width;
27977                     
27978                     if(width < height){
27979                         scale = targetHeight / height;
27980                     }
27981                 }
27982                 
27983                 context.scale(scale, scale);
27984                 
27985                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27986                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27987
27988                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27989                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27990                 
27991                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27992                 
27993                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27994                 
27995                 break;
27996             default : 
27997                 break;
27998         }
27999         
28000         this.cropData = canvas.toDataURL(this.cropType);
28001         
28002         if(this.fireEvent('crop', this, this.cropData) !== false){
28003             this.process(this.file, this.cropData);
28004         }
28005         
28006         return;
28007         
28008     },
28009     
28010     setThumbBoxSize : function()
28011     {
28012         var width, height;
28013         
28014         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28015             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28016             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28017             
28018             this.minWidth = width;
28019             this.minHeight = height;
28020             
28021             if(this.rotate == 90 || this.rotate == 270){
28022                 this.minWidth = height;
28023                 this.minHeight = width;
28024             }
28025         }
28026         
28027         height = 300;
28028         width = Math.ceil(this.minWidth * height / this.minHeight);
28029         
28030         if(this.minWidth > this.minHeight){
28031             width = 300;
28032             height = Math.ceil(this.minHeight * width / this.minWidth);
28033         }
28034         
28035         this.thumbEl.setStyle({
28036             width : width + 'px',
28037             height : height + 'px'
28038         });
28039
28040         return;
28041             
28042     },
28043     
28044     setThumbBoxPosition : function()
28045     {
28046         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28047         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28048         
28049         this.thumbEl.setLeft(x);
28050         this.thumbEl.setTop(y);
28051         
28052     },
28053     
28054     baseRotateLevel : function()
28055     {
28056         this.baseRotate = 1;
28057         
28058         if(
28059                 typeof(this.exif) != 'undefined' &&
28060                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28061                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28062         ){
28063             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28064         }
28065         
28066         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28067         
28068     },
28069     
28070     baseScaleLevel : function()
28071     {
28072         var width, height;
28073         
28074         if(this.isDocument){
28075             
28076             if(this.baseRotate == 6 || this.baseRotate == 8){
28077             
28078                 height = this.thumbEl.getHeight();
28079                 this.baseScale = height / this.imageEl.OriginWidth;
28080
28081                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28082                     width = this.thumbEl.getWidth();
28083                     this.baseScale = width / this.imageEl.OriginHeight;
28084                 }
28085
28086                 return;
28087             }
28088
28089             height = this.thumbEl.getHeight();
28090             this.baseScale = height / this.imageEl.OriginHeight;
28091
28092             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28093                 width = this.thumbEl.getWidth();
28094                 this.baseScale = width / this.imageEl.OriginWidth;
28095             }
28096
28097             return;
28098         }
28099         
28100         if(this.baseRotate == 6 || this.baseRotate == 8){
28101             
28102             width = this.thumbEl.getHeight();
28103             this.baseScale = width / this.imageEl.OriginHeight;
28104             
28105             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28106                 height = this.thumbEl.getWidth();
28107                 this.baseScale = height / this.imageEl.OriginHeight;
28108             }
28109             
28110             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28111                 height = this.thumbEl.getWidth();
28112                 this.baseScale = height / this.imageEl.OriginHeight;
28113                 
28114                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28115                     width = this.thumbEl.getHeight();
28116                     this.baseScale = width / this.imageEl.OriginWidth;
28117                 }
28118             }
28119             
28120             return;
28121         }
28122         
28123         width = this.thumbEl.getWidth();
28124         this.baseScale = width / this.imageEl.OriginWidth;
28125         
28126         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28127             height = this.thumbEl.getHeight();
28128             this.baseScale = height / this.imageEl.OriginHeight;
28129         }
28130         
28131         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28132             
28133             height = this.thumbEl.getHeight();
28134             this.baseScale = height / this.imageEl.OriginHeight;
28135             
28136             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28137                 width = this.thumbEl.getWidth();
28138                 this.baseScale = width / this.imageEl.OriginWidth;
28139             }
28140             
28141         }
28142         
28143         return;
28144     },
28145     
28146     getScaleLevel : function()
28147     {
28148         return this.baseScale * Math.pow(1.1, this.scale);
28149     },
28150     
28151     onTouchStart : function(e)
28152     {
28153         if(!this.canvasLoaded){
28154             this.beforeSelectFile(e);
28155             return;
28156         }
28157         
28158         var touches = e.browserEvent.touches;
28159         
28160         if(!touches){
28161             return;
28162         }
28163         
28164         if(touches.length == 1){
28165             this.onMouseDown(e);
28166             return;
28167         }
28168         
28169         if(touches.length != 2){
28170             return;
28171         }
28172         
28173         var coords = [];
28174         
28175         for(var i = 0, finger; finger = touches[i]; i++){
28176             coords.push(finger.pageX, finger.pageY);
28177         }
28178         
28179         var x = Math.pow(coords[0] - coords[2], 2);
28180         var y = Math.pow(coords[1] - coords[3], 2);
28181         
28182         this.startDistance = Math.sqrt(x + y);
28183         
28184         this.startScale = this.scale;
28185         
28186         this.pinching = true;
28187         this.dragable = false;
28188         
28189     },
28190     
28191     onTouchMove : function(e)
28192     {
28193         if(!this.pinching && !this.dragable){
28194             return;
28195         }
28196         
28197         var touches = e.browserEvent.touches;
28198         
28199         if(!touches){
28200             return;
28201         }
28202         
28203         if(this.dragable){
28204             this.onMouseMove(e);
28205             return;
28206         }
28207         
28208         var coords = [];
28209         
28210         for(var i = 0, finger; finger = touches[i]; i++){
28211             coords.push(finger.pageX, finger.pageY);
28212         }
28213         
28214         var x = Math.pow(coords[0] - coords[2], 2);
28215         var y = Math.pow(coords[1] - coords[3], 2);
28216         
28217         this.endDistance = Math.sqrt(x + y);
28218         
28219         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28220         
28221         if(!this.zoomable()){
28222             this.scale = this.startScale;
28223             return;
28224         }
28225         
28226         this.draw();
28227         
28228     },
28229     
28230     onTouchEnd : function(e)
28231     {
28232         this.pinching = false;
28233         this.dragable = false;
28234         
28235     },
28236     
28237     process : function(file, crop)
28238     {
28239         if(this.loadMask){
28240             this.maskEl.mask(this.loadingText);
28241         }
28242         
28243         this.xhr = new XMLHttpRequest();
28244         
28245         file.xhr = this.xhr;
28246
28247         this.xhr.open(this.method, this.url, true);
28248         
28249         var headers = {
28250             "Accept": "application/json",
28251             "Cache-Control": "no-cache",
28252             "X-Requested-With": "XMLHttpRequest"
28253         };
28254         
28255         for (var headerName in headers) {
28256             var headerValue = headers[headerName];
28257             if (headerValue) {
28258                 this.xhr.setRequestHeader(headerName, headerValue);
28259             }
28260         }
28261         
28262         var _this = this;
28263         
28264         this.xhr.onload = function()
28265         {
28266             _this.xhrOnLoad(_this.xhr);
28267         }
28268         
28269         this.xhr.onerror = function()
28270         {
28271             _this.xhrOnError(_this.xhr);
28272         }
28273         
28274         var formData = new FormData();
28275
28276         formData.append('returnHTML', 'NO');
28277         
28278         if(crop){
28279             formData.append('crop', crop);
28280         }
28281         
28282         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28283             formData.append(this.paramName, file, file.name);
28284         }
28285         
28286         if(typeof(file.filename) != 'undefined'){
28287             formData.append('filename', file.filename);
28288         }
28289         
28290         if(typeof(file.mimetype) != 'undefined'){
28291             formData.append('mimetype', file.mimetype);
28292         }
28293         
28294         if(this.fireEvent('arrange', this, formData) != false){
28295             this.xhr.send(formData);
28296         };
28297     },
28298     
28299     xhrOnLoad : function(xhr)
28300     {
28301         if(this.loadMask){
28302             this.maskEl.unmask();
28303         }
28304         
28305         if (xhr.readyState !== 4) {
28306             this.fireEvent('exception', this, xhr);
28307             return;
28308         }
28309
28310         var response = Roo.decode(xhr.responseText);
28311         
28312         if(!response.success){
28313             this.fireEvent('exception', this, xhr);
28314             return;
28315         }
28316         
28317         var response = Roo.decode(xhr.responseText);
28318         
28319         this.fireEvent('upload', this, response);
28320         
28321     },
28322     
28323     xhrOnError : function()
28324     {
28325         if(this.loadMask){
28326             this.maskEl.unmask();
28327         }
28328         
28329         Roo.log('xhr on error');
28330         
28331         var response = Roo.decode(xhr.responseText);
28332           
28333         Roo.log(response);
28334         
28335     },
28336     
28337     prepare : function(file)
28338     {   
28339         if(this.loadMask){
28340             this.maskEl.mask(this.loadingText);
28341         }
28342         
28343         this.file = false;
28344         this.exif = {};
28345         
28346         if(typeof(file) === 'string'){
28347             this.loadCanvas(file);
28348             return;
28349         }
28350         
28351         if(!file || !this.urlAPI){
28352             return;
28353         }
28354         
28355         this.file = file;
28356         this.cropType = file.type;
28357         
28358         var _this = this;
28359         
28360         if(this.fireEvent('prepare', this, this.file) != false){
28361             
28362             var reader = new FileReader();
28363             
28364             reader.onload = function (e) {
28365                 if (e.target.error) {
28366                     Roo.log(e.target.error);
28367                     return;
28368                 }
28369                 
28370                 var buffer = e.target.result,
28371                     dataView = new DataView(buffer),
28372                     offset = 2,
28373                     maxOffset = dataView.byteLength - 4,
28374                     markerBytes,
28375                     markerLength;
28376                 
28377                 if (dataView.getUint16(0) === 0xffd8) {
28378                     while (offset < maxOffset) {
28379                         markerBytes = dataView.getUint16(offset);
28380                         
28381                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28382                             markerLength = dataView.getUint16(offset + 2) + 2;
28383                             if (offset + markerLength > dataView.byteLength) {
28384                                 Roo.log('Invalid meta data: Invalid segment size.');
28385                                 break;
28386                             }
28387                             
28388                             if(markerBytes == 0xffe1){
28389                                 _this.parseExifData(
28390                                     dataView,
28391                                     offset,
28392                                     markerLength
28393                                 );
28394                             }
28395                             
28396                             offset += markerLength;
28397                             
28398                             continue;
28399                         }
28400                         
28401                         break;
28402                     }
28403                     
28404                 }
28405                 
28406                 var url = _this.urlAPI.createObjectURL(_this.file);
28407                 
28408                 _this.loadCanvas(url);
28409                 
28410                 return;
28411             }
28412             
28413             reader.readAsArrayBuffer(this.file);
28414             
28415         }
28416         
28417     },
28418     
28419     parseExifData : function(dataView, offset, length)
28420     {
28421         var tiffOffset = offset + 10,
28422             littleEndian,
28423             dirOffset;
28424     
28425         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28426             // No Exif data, might be XMP data instead
28427             return;
28428         }
28429         
28430         // Check for the ASCII code for "Exif" (0x45786966):
28431         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28432             // No Exif data, might be XMP data instead
28433             return;
28434         }
28435         if (tiffOffset + 8 > dataView.byteLength) {
28436             Roo.log('Invalid Exif data: Invalid segment size.');
28437             return;
28438         }
28439         // Check for the two null bytes:
28440         if (dataView.getUint16(offset + 8) !== 0x0000) {
28441             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28442             return;
28443         }
28444         // Check the byte alignment:
28445         switch (dataView.getUint16(tiffOffset)) {
28446         case 0x4949:
28447             littleEndian = true;
28448             break;
28449         case 0x4D4D:
28450             littleEndian = false;
28451             break;
28452         default:
28453             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28454             return;
28455         }
28456         // Check for the TIFF tag marker (0x002A):
28457         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28458             Roo.log('Invalid Exif data: Missing TIFF marker.');
28459             return;
28460         }
28461         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28462         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28463         
28464         this.parseExifTags(
28465             dataView,
28466             tiffOffset,
28467             tiffOffset + dirOffset,
28468             littleEndian
28469         );
28470     },
28471     
28472     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28473     {
28474         var tagsNumber,
28475             dirEndOffset,
28476             i;
28477         if (dirOffset + 6 > dataView.byteLength) {
28478             Roo.log('Invalid Exif data: Invalid directory offset.');
28479             return;
28480         }
28481         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28482         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28483         if (dirEndOffset + 4 > dataView.byteLength) {
28484             Roo.log('Invalid Exif data: Invalid directory size.');
28485             return;
28486         }
28487         for (i = 0; i < tagsNumber; i += 1) {
28488             this.parseExifTag(
28489                 dataView,
28490                 tiffOffset,
28491                 dirOffset + 2 + 12 * i, // tag offset
28492                 littleEndian
28493             );
28494         }
28495         // Return the offset to the next directory:
28496         return dataView.getUint32(dirEndOffset, littleEndian);
28497     },
28498     
28499     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28500     {
28501         var tag = dataView.getUint16(offset, littleEndian);
28502         
28503         this.exif[tag] = this.getExifValue(
28504             dataView,
28505             tiffOffset,
28506             offset,
28507             dataView.getUint16(offset + 2, littleEndian), // tag type
28508             dataView.getUint32(offset + 4, littleEndian), // tag length
28509             littleEndian
28510         );
28511     },
28512     
28513     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28514     {
28515         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28516             tagSize,
28517             dataOffset,
28518             values,
28519             i,
28520             str,
28521             c;
28522     
28523         if (!tagType) {
28524             Roo.log('Invalid Exif data: Invalid tag type.');
28525             return;
28526         }
28527         
28528         tagSize = tagType.size * length;
28529         // Determine if the value is contained in the dataOffset bytes,
28530         // or if the value at the dataOffset is a pointer to the actual data:
28531         dataOffset = tagSize > 4 ?
28532                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28533         if (dataOffset + tagSize > dataView.byteLength) {
28534             Roo.log('Invalid Exif data: Invalid data offset.');
28535             return;
28536         }
28537         if (length === 1) {
28538             return tagType.getValue(dataView, dataOffset, littleEndian);
28539         }
28540         values = [];
28541         for (i = 0; i < length; i += 1) {
28542             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28543         }
28544         
28545         if (tagType.ascii) {
28546             str = '';
28547             // Concatenate the chars:
28548             for (i = 0; i < values.length; i += 1) {
28549                 c = values[i];
28550                 // Ignore the terminating NULL byte(s):
28551                 if (c === '\u0000') {
28552                     break;
28553                 }
28554                 str += c;
28555             }
28556             return str;
28557         }
28558         return values;
28559     }
28560     
28561 });
28562
28563 Roo.apply(Roo.bootstrap.UploadCropbox, {
28564     tags : {
28565         'Orientation': 0x0112
28566     },
28567     
28568     Orientation: {
28569             1: 0, //'top-left',
28570 //            2: 'top-right',
28571             3: 180, //'bottom-right',
28572 //            4: 'bottom-left',
28573 //            5: 'left-top',
28574             6: 90, //'right-top',
28575 //            7: 'right-bottom',
28576             8: 270 //'left-bottom'
28577     },
28578     
28579     exifTagTypes : {
28580         // byte, 8-bit unsigned int:
28581         1: {
28582             getValue: function (dataView, dataOffset) {
28583                 return dataView.getUint8(dataOffset);
28584             },
28585             size: 1
28586         },
28587         // ascii, 8-bit byte:
28588         2: {
28589             getValue: function (dataView, dataOffset) {
28590                 return String.fromCharCode(dataView.getUint8(dataOffset));
28591             },
28592             size: 1,
28593             ascii: true
28594         },
28595         // short, 16 bit int:
28596         3: {
28597             getValue: function (dataView, dataOffset, littleEndian) {
28598                 return dataView.getUint16(dataOffset, littleEndian);
28599             },
28600             size: 2
28601         },
28602         // long, 32 bit int:
28603         4: {
28604             getValue: function (dataView, dataOffset, littleEndian) {
28605                 return dataView.getUint32(dataOffset, littleEndian);
28606             },
28607             size: 4
28608         },
28609         // rational = two long values, first is numerator, second is denominator:
28610         5: {
28611             getValue: function (dataView, dataOffset, littleEndian) {
28612                 return dataView.getUint32(dataOffset, littleEndian) /
28613                     dataView.getUint32(dataOffset + 4, littleEndian);
28614             },
28615             size: 8
28616         },
28617         // slong, 32 bit signed int:
28618         9: {
28619             getValue: function (dataView, dataOffset, littleEndian) {
28620                 return dataView.getInt32(dataOffset, littleEndian);
28621             },
28622             size: 4
28623         },
28624         // srational, two slongs, first is numerator, second is denominator:
28625         10: {
28626             getValue: function (dataView, dataOffset, littleEndian) {
28627                 return dataView.getInt32(dataOffset, littleEndian) /
28628                     dataView.getInt32(dataOffset + 4, littleEndian);
28629             },
28630             size: 8
28631         }
28632     },
28633     
28634     footer : {
28635         STANDARD : [
28636             {
28637                 tag : 'div',
28638                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28639                 action : 'rotate-left',
28640                 cn : [
28641                     {
28642                         tag : 'button',
28643                         cls : 'btn btn-default',
28644                         html : '<i class="fa fa-undo"></i>'
28645                     }
28646                 ]
28647             },
28648             {
28649                 tag : 'div',
28650                 cls : 'btn-group roo-upload-cropbox-picture',
28651                 action : 'picture',
28652                 cn : [
28653                     {
28654                         tag : 'button',
28655                         cls : 'btn btn-default',
28656                         html : '<i class="fa fa-picture-o"></i>'
28657                     }
28658                 ]
28659             },
28660             {
28661                 tag : 'div',
28662                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28663                 action : 'rotate-right',
28664                 cn : [
28665                     {
28666                         tag : 'button',
28667                         cls : 'btn btn-default',
28668                         html : '<i class="fa fa-repeat"></i>'
28669                     }
28670                 ]
28671             }
28672         ],
28673         DOCUMENT : [
28674             {
28675                 tag : 'div',
28676                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28677                 action : 'rotate-left',
28678                 cn : [
28679                     {
28680                         tag : 'button',
28681                         cls : 'btn btn-default',
28682                         html : '<i class="fa fa-undo"></i>'
28683                     }
28684                 ]
28685             },
28686             {
28687                 tag : 'div',
28688                 cls : 'btn-group roo-upload-cropbox-download',
28689                 action : 'download',
28690                 cn : [
28691                     {
28692                         tag : 'button',
28693                         cls : 'btn btn-default',
28694                         html : '<i class="fa fa-download"></i>'
28695                     }
28696                 ]
28697             },
28698             {
28699                 tag : 'div',
28700                 cls : 'btn-group roo-upload-cropbox-crop',
28701                 action : 'crop',
28702                 cn : [
28703                     {
28704                         tag : 'button',
28705                         cls : 'btn btn-default',
28706                         html : '<i class="fa fa-crop"></i>'
28707                     }
28708                 ]
28709             },
28710             {
28711                 tag : 'div',
28712                 cls : 'btn-group roo-upload-cropbox-trash',
28713                 action : 'trash',
28714                 cn : [
28715                     {
28716                         tag : 'button',
28717                         cls : 'btn btn-default',
28718                         html : '<i class="fa fa-trash"></i>'
28719                     }
28720                 ]
28721             },
28722             {
28723                 tag : 'div',
28724                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28725                 action : 'rotate-right',
28726                 cn : [
28727                     {
28728                         tag : 'button',
28729                         cls : 'btn btn-default',
28730                         html : '<i class="fa fa-repeat"></i>'
28731                     }
28732                 ]
28733             }
28734         ],
28735         ROTATOR : [
28736             {
28737                 tag : 'div',
28738                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28739                 action : 'rotate-left',
28740                 cn : [
28741                     {
28742                         tag : 'button',
28743                         cls : 'btn btn-default',
28744                         html : '<i class="fa fa-undo"></i>'
28745                     }
28746                 ]
28747             },
28748             {
28749                 tag : 'div',
28750                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28751                 action : 'rotate-right',
28752                 cn : [
28753                     {
28754                         tag : 'button',
28755                         cls : 'btn btn-default',
28756                         html : '<i class="fa fa-repeat"></i>'
28757                     }
28758                 ]
28759             }
28760         ]
28761     }
28762 });
28763
28764 /*
28765 * Licence: LGPL
28766 */
28767
28768 /**
28769  * @class Roo.bootstrap.DocumentManager
28770  * @extends Roo.bootstrap.Component
28771  * Bootstrap DocumentManager class
28772  * @cfg {String} paramName default 'imageUpload'
28773  * @cfg {String} toolTipName default 'filename'
28774  * @cfg {String} method default POST
28775  * @cfg {String} url action url
28776  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28777  * @cfg {Boolean} multiple multiple upload default true
28778  * @cfg {Number} thumbSize default 300
28779  * @cfg {String} fieldLabel
28780  * @cfg {Number} labelWidth default 4
28781  * @cfg {String} labelAlign (left|top) default left
28782  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28783 * @cfg {Number} labellg set the width of label (1-12)
28784  * @cfg {Number} labelmd set the width of label (1-12)
28785  * @cfg {Number} labelsm set the width of label (1-12)
28786  * @cfg {Number} labelxs set the width of label (1-12)
28787  * 
28788  * @constructor
28789  * Create a new DocumentManager
28790  * @param {Object} config The config object
28791  */
28792
28793 Roo.bootstrap.DocumentManager = function(config){
28794     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28795     
28796     this.files = [];
28797     this.delegates = [];
28798     
28799     this.addEvents({
28800         /**
28801          * @event initial
28802          * Fire when initial the DocumentManager
28803          * @param {Roo.bootstrap.DocumentManager} this
28804          */
28805         "initial" : true,
28806         /**
28807          * @event inspect
28808          * inspect selected file
28809          * @param {Roo.bootstrap.DocumentManager} this
28810          * @param {File} file
28811          */
28812         "inspect" : true,
28813         /**
28814          * @event exception
28815          * Fire when xhr load exception
28816          * @param {Roo.bootstrap.DocumentManager} this
28817          * @param {XMLHttpRequest} xhr
28818          */
28819         "exception" : true,
28820         /**
28821          * @event afterupload
28822          * Fire when xhr load exception
28823          * @param {Roo.bootstrap.DocumentManager} this
28824          * @param {XMLHttpRequest} xhr
28825          */
28826         "afterupload" : true,
28827         /**
28828          * @event prepare
28829          * prepare the form data
28830          * @param {Roo.bootstrap.DocumentManager} this
28831          * @param {Object} formData
28832          */
28833         "prepare" : true,
28834         /**
28835          * @event remove
28836          * Fire when remove the file
28837          * @param {Roo.bootstrap.DocumentManager} this
28838          * @param {Object} file
28839          */
28840         "remove" : true,
28841         /**
28842          * @event refresh
28843          * Fire after refresh the file
28844          * @param {Roo.bootstrap.DocumentManager} this
28845          */
28846         "refresh" : true,
28847         /**
28848          * @event click
28849          * Fire after click the image
28850          * @param {Roo.bootstrap.DocumentManager} this
28851          * @param {Object} file
28852          */
28853         "click" : true,
28854         /**
28855          * @event edit
28856          * Fire when upload a image and editable set to true
28857          * @param {Roo.bootstrap.DocumentManager} this
28858          * @param {Object} file
28859          */
28860         "edit" : true,
28861         /**
28862          * @event beforeselectfile
28863          * Fire before select file
28864          * @param {Roo.bootstrap.DocumentManager} this
28865          */
28866         "beforeselectfile" : true,
28867         /**
28868          * @event process
28869          * Fire before process file
28870          * @param {Roo.bootstrap.DocumentManager} this
28871          * @param {Object} file
28872          */
28873         "process" : true,
28874         /**
28875          * @event previewrendered
28876          * Fire when preview rendered
28877          * @param {Roo.bootstrap.DocumentManager} this
28878          * @param {Object} file
28879          */
28880         "previewrendered" : true,
28881         /**
28882          */
28883         "previewResize" : true
28884         
28885     });
28886 };
28887
28888 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28889     
28890     boxes : 0,
28891     inputName : '',
28892     thumbSize : 300,
28893     multiple : true,
28894     files : false,
28895     method : 'POST',
28896     url : '',
28897     paramName : 'imageUpload',
28898     toolTipName : 'filename',
28899     fieldLabel : '',
28900     labelWidth : 4,
28901     labelAlign : 'left',
28902     editable : true,
28903     delegates : false,
28904     xhr : false, 
28905     
28906     labellg : 0,
28907     labelmd : 0,
28908     labelsm : 0,
28909     labelxs : 0,
28910     
28911     getAutoCreate : function()
28912     {   
28913         var managerWidget = {
28914             tag : 'div',
28915             cls : 'roo-document-manager',
28916             cn : [
28917                 {
28918                     tag : 'input',
28919                     cls : 'roo-document-manager-selector',
28920                     type : 'file'
28921                 },
28922                 {
28923                     tag : 'div',
28924                     cls : 'roo-document-manager-uploader',
28925                     cn : [
28926                         {
28927                             tag : 'div',
28928                             cls : 'roo-document-manager-upload-btn',
28929                             html : '<i class="fa fa-plus"></i>'
28930                         }
28931                     ]
28932                     
28933                 }
28934             ]
28935         };
28936         
28937         var content = [
28938             {
28939                 tag : 'div',
28940                 cls : 'column col-md-12',
28941                 cn : managerWidget
28942             }
28943         ];
28944         
28945         if(this.fieldLabel.length){
28946             
28947             content = [
28948                 {
28949                     tag : 'div',
28950                     cls : 'column col-md-12',
28951                     html : this.fieldLabel
28952                 },
28953                 {
28954                     tag : 'div',
28955                     cls : 'column col-md-12',
28956                     cn : managerWidget
28957                 }
28958             ];
28959
28960             if(this.labelAlign == 'left'){
28961                 content = [
28962                     {
28963                         tag : 'div',
28964                         cls : 'column',
28965                         html : this.fieldLabel
28966                     },
28967                     {
28968                         tag : 'div',
28969                         cls : 'column',
28970                         cn : managerWidget
28971                     }
28972                 ];
28973                 
28974                 if(this.labelWidth > 12){
28975                     content[0].style = "width: " + this.labelWidth + 'px';
28976                 }
28977
28978                 if(this.labelWidth < 13 && this.labelmd == 0){
28979                     this.labelmd = this.labelWidth;
28980                 }
28981
28982                 if(this.labellg > 0){
28983                     content[0].cls += ' col-lg-' + this.labellg;
28984                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28985                 }
28986
28987                 if(this.labelmd > 0){
28988                     content[0].cls += ' col-md-' + this.labelmd;
28989                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28990                 }
28991
28992                 if(this.labelsm > 0){
28993                     content[0].cls += ' col-sm-' + this.labelsm;
28994                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28995                 }
28996
28997                 if(this.labelxs > 0){
28998                     content[0].cls += ' col-xs-' + this.labelxs;
28999                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29000                 }
29001                 
29002             }
29003         }
29004         
29005         var cfg = {
29006             tag : 'div',
29007             cls : 'row clearfix',
29008             cn : content
29009         };
29010         
29011         return cfg;
29012         
29013     },
29014     
29015     initEvents : function()
29016     {
29017         this.managerEl = this.el.select('.roo-document-manager', true).first();
29018         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29019         
29020         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29021         this.selectorEl.hide();
29022         
29023         if(this.multiple){
29024             this.selectorEl.attr('multiple', 'multiple');
29025         }
29026         
29027         this.selectorEl.on('change', this.onFileSelected, this);
29028         
29029         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29030         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29031         
29032         this.uploader.on('click', this.onUploaderClick, this);
29033         
29034         this.renderProgressDialog();
29035         
29036         var _this = this;
29037         
29038         window.addEventListener("resize", function() { _this.refresh(); } );
29039         
29040         this.fireEvent('initial', this);
29041     },
29042     
29043     renderProgressDialog : function()
29044     {
29045         var _this = this;
29046         
29047         this.progressDialog = new Roo.bootstrap.Modal({
29048             cls : 'roo-document-manager-progress-dialog',
29049             allow_close : false,
29050             title : '',
29051             buttons : [
29052                 {
29053                     name  :'cancel',
29054                     weight : 'danger',
29055                     html : 'Cancel'
29056                 }
29057             ], 
29058             listeners : { 
29059                 btnclick : function() {
29060                     _this.uploadCancel();
29061                     this.hide();
29062                 }
29063             }
29064         });
29065          
29066         this.progressDialog.render(Roo.get(document.body));
29067          
29068         this.progress = new Roo.bootstrap.Progress({
29069             cls : 'roo-document-manager-progress',
29070             active : true,
29071             striped : true
29072         });
29073         
29074         this.progress.render(this.progressDialog.getChildContainer());
29075         
29076         this.progressBar = new Roo.bootstrap.ProgressBar({
29077             cls : 'roo-document-manager-progress-bar',
29078             aria_valuenow : 0,
29079             aria_valuemin : 0,
29080             aria_valuemax : 12,
29081             panel : 'success'
29082         });
29083         
29084         this.progressBar.render(this.progress.getChildContainer());
29085     },
29086     
29087     onUploaderClick : function(e)
29088     {
29089         e.preventDefault();
29090      
29091         if(this.fireEvent('beforeselectfile', this) != false){
29092             this.selectorEl.dom.click();
29093         }
29094         
29095     },
29096     
29097     onFileSelected : function(e)
29098     {
29099         e.preventDefault();
29100         
29101         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29102             return;
29103         }
29104         
29105         Roo.each(this.selectorEl.dom.files, function(file){
29106             if(this.fireEvent('inspect', this, file) != false){
29107                 this.files.push(file);
29108             }
29109         }, this);
29110         
29111         this.queue();
29112         
29113     },
29114     
29115     queue : function()
29116     {
29117         this.selectorEl.dom.value = '';
29118         
29119         if(!this.files || !this.files.length){
29120             return;
29121         }
29122         
29123         if(this.boxes > 0 && this.files.length > this.boxes){
29124             this.files = this.files.slice(0, this.boxes);
29125         }
29126         
29127         this.uploader.show();
29128         
29129         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29130             this.uploader.hide();
29131         }
29132         
29133         var _this = this;
29134         
29135         var files = [];
29136         
29137         var docs = [];
29138         
29139         Roo.each(this.files, function(file){
29140             
29141             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29142                 var f = this.renderPreview(file);
29143                 files.push(f);
29144                 return;
29145             }
29146             
29147             if(file.type.indexOf('image') != -1){
29148                 this.delegates.push(
29149                     (function(){
29150                         _this.process(file);
29151                     }).createDelegate(this)
29152                 );
29153         
29154                 return;
29155             }
29156             
29157             docs.push(
29158                 (function(){
29159                     _this.process(file);
29160                 }).createDelegate(this)
29161             );
29162             
29163         }, this);
29164         
29165         this.files = files;
29166         
29167         this.delegates = this.delegates.concat(docs);
29168         
29169         if(!this.delegates.length){
29170             this.refresh();
29171             return;
29172         }
29173         
29174         this.progressBar.aria_valuemax = this.delegates.length;
29175         
29176         this.arrange();
29177         
29178         return;
29179     },
29180     
29181     arrange : function()
29182     {
29183         if(!this.delegates.length){
29184             this.progressDialog.hide();
29185             this.refresh();
29186             return;
29187         }
29188         
29189         var delegate = this.delegates.shift();
29190         
29191         this.progressDialog.show();
29192         
29193         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29194         
29195         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29196         
29197         delegate();
29198     },
29199     
29200     refresh : function()
29201     {
29202         this.uploader.show();
29203         
29204         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29205             this.uploader.hide();
29206         }
29207         
29208         Roo.isTouch ? this.closable(false) : this.closable(true);
29209         
29210         this.fireEvent('refresh', this);
29211     },
29212     
29213     onRemove : function(e, el, o)
29214     {
29215         e.preventDefault();
29216         
29217         this.fireEvent('remove', this, o);
29218         
29219     },
29220     
29221     remove : function(o)
29222     {
29223         var files = [];
29224         
29225         Roo.each(this.files, function(file){
29226             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29227                 files.push(file);
29228                 return;
29229             }
29230
29231             o.target.remove();
29232
29233         }, this);
29234         
29235         this.files = files;
29236         
29237         this.refresh();
29238     },
29239     
29240     clear : function()
29241     {
29242         Roo.each(this.files, function(file){
29243             if(!file.target){
29244                 return;
29245             }
29246             
29247             file.target.remove();
29248
29249         }, this);
29250         
29251         this.files = [];
29252         
29253         this.refresh();
29254     },
29255     
29256     onClick : function(e, el, o)
29257     {
29258         e.preventDefault();
29259         
29260         this.fireEvent('click', this, o);
29261         
29262     },
29263     
29264     closable : function(closable)
29265     {
29266         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29267             
29268             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29269             
29270             if(closable){
29271                 el.show();
29272                 return;
29273             }
29274             
29275             el.hide();
29276             
29277         }, this);
29278     },
29279     
29280     xhrOnLoad : function(xhr)
29281     {
29282         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29283             el.remove();
29284         }, this);
29285         
29286         if (xhr.readyState !== 4) {
29287             this.arrange();
29288             this.fireEvent('exception', this, xhr);
29289             return;
29290         }
29291
29292         var response = Roo.decode(xhr.responseText);
29293         
29294         if(!response.success){
29295             this.arrange();
29296             this.fireEvent('exception', this, xhr);
29297             return;
29298         }
29299         
29300         var file = this.renderPreview(response.data);
29301         
29302         this.files.push(file);
29303         
29304         this.arrange();
29305         
29306         this.fireEvent('afterupload', this, xhr);
29307         
29308     },
29309     
29310     xhrOnError : function(xhr)
29311     {
29312         Roo.log('xhr on error');
29313         
29314         var response = Roo.decode(xhr.responseText);
29315           
29316         Roo.log(response);
29317         
29318         this.arrange();
29319     },
29320     
29321     process : function(file)
29322     {
29323         if(this.fireEvent('process', this, file) !== false){
29324             if(this.editable && file.type.indexOf('image') != -1){
29325                 this.fireEvent('edit', this, file);
29326                 return;
29327             }
29328
29329             this.uploadStart(file, false);
29330
29331             return;
29332         }
29333         
29334     },
29335     
29336     uploadStart : function(file, crop)
29337     {
29338         this.xhr = new XMLHttpRequest();
29339         
29340         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29341             this.arrange();
29342             return;
29343         }
29344         
29345         file.xhr = this.xhr;
29346             
29347         this.managerEl.createChild({
29348             tag : 'div',
29349             cls : 'roo-document-manager-loading',
29350             cn : [
29351                 {
29352                     tag : 'div',
29353                     tooltip : file.name,
29354                     cls : 'roo-document-manager-thumb',
29355                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29356                 }
29357             ]
29358
29359         });
29360
29361         this.xhr.open(this.method, this.url, true);
29362         
29363         var headers = {
29364             "Accept": "application/json",
29365             "Cache-Control": "no-cache",
29366             "X-Requested-With": "XMLHttpRequest"
29367         };
29368         
29369         for (var headerName in headers) {
29370             var headerValue = headers[headerName];
29371             if (headerValue) {
29372                 this.xhr.setRequestHeader(headerName, headerValue);
29373             }
29374         }
29375         
29376         var _this = this;
29377         
29378         this.xhr.onload = function()
29379         {
29380             _this.xhrOnLoad(_this.xhr);
29381         }
29382         
29383         this.xhr.onerror = function()
29384         {
29385             _this.xhrOnError(_this.xhr);
29386         }
29387         
29388         var formData = new FormData();
29389
29390         formData.append('returnHTML', 'NO');
29391         
29392         if(crop){
29393             formData.append('crop', crop);
29394         }
29395         
29396         formData.append(this.paramName, file, file.name);
29397         
29398         var options = {
29399             file : file, 
29400             manually : false
29401         };
29402         
29403         if(this.fireEvent('prepare', this, formData, options) != false){
29404             
29405             if(options.manually){
29406                 return;
29407             }
29408             
29409             this.xhr.send(formData);
29410             return;
29411         };
29412         
29413         this.uploadCancel();
29414     },
29415     
29416     uploadCancel : function()
29417     {
29418         if (this.xhr) {
29419             this.xhr.abort();
29420         }
29421         
29422         this.delegates = [];
29423         
29424         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29425             el.remove();
29426         }, this);
29427         
29428         this.arrange();
29429     },
29430     
29431     renderPreview : function(file)
29432     {
29433         if(typeof(file.target) != 'undefined' && file.target){
29434             return file;
29435         }
29436         
29437         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29438         
29439         var previewEl = this.managerEl.createChild({
29440             tag : 'div',
29441             cls : 'roo-document-manager-preview',
29442             cn : [
29443                 {
29444                     tag : 'div',
29445                     tooltip : file[this.toolTipName],
29446                     cls : 'roo-document-manager-thumb',
29447                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29448                 },
29449                 {
29450                     tag : 'button',
29451                     cls : 'close',
29452                     html : '<i class="fa fa-times-circle"></i>'
29453                 }
29454             ]
29455         });
29456
29457         var close = previewEl.select('button.close', true).first();
29458
29459         close.on('click', this.onRemove, this, file);
29460
29461         file.target = previewEl;
29462
29463         var image = previewEl.select('img', true).first();
29464         
29465         var _this = this;
29466         
29467         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29468         
29469         image.on('click', this.onClick, this, file);
29470         
29471         this.fireEvent('previewrendered', this, file);
29472         
29473         return file;
29474         
29475     },
29476     
29477     onPreviewLoad : function(file, image)
29478     {
29479         if(typeof(file.target) == 'undefined' || !file.target){
29480             return;
29481         }
29482         
29483         var width = image.dom.naturalWidth || image.dom.width;
29484         var height = image.dom.naturalHeight || image.dom.height;
29485         
29486         if(!this.previewResize) {
29487             return;
29488         }
29489         
29490         if(width > height){
29491             file.target.addClass('wide');
29492             return;
29493         }
29494         
29495         file.target.addClass('tall');
29496         return;
29497         
29498     },
29499     
29500     uploadFromSource : function(file, crop)
29501     {
29502         this.xhr = new XMLHttpRequest();
29503         
29504         this.managerEl.createChild({
29505             tag : 'div',
29506             cls : 'roo-document-manager-loading',
29507             cn : [
29508                 {
29509                     tag : 'div',
29510                     tooltip : file.name,
29511                     cls : 'roo-document-manager-thumb',
29512                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29513                 }
29514             ]
29515
29516         });
29517
29518         this.xhr.open(this.method, this.url, true);
29519         
29520         var headers = {
29521             "Accept": "application/json",
29522             "Cache-Control": "no-cache",
29523             "X-Requested-With": "XMLHttpRequest"
29524         };
29525         
29526         for (var headerName in headers) {
29527             var headerValue = headers[headerName];
29528             if (headerValue) {
29529                 this.xhr.setRequestHeader(headerName, headerValue);
29530             }
29531         }
29532         
29533         var _this = this;
29534         
29535         this.xhr.onload = function()
29536         {
29537             _this.xhrOnLoad(_this.xhr);
29538         }
29539         
29540         this.xhr.onerror = function()
29541         {
29542             _this.xhrOnError(_this.xhr);
29543         }
29544         
29545         var formData = new FormData();
29546
29547         formData.append('returnHTML', 'NO');
29548         
29549         formData.append('crop', crop);
29550         
29551         if(typeof(file.filename) != 'undefined'){
29552             formData.append('filename', file.filename);
29553         }
29554         
29555         if(typeof(file.mimetype) != 'undefined'){
29556             formData.append('mimetype', file.mimetype);
29557         }
29558         
29559         Roo.log(formData);
29560         
29561         if(this.fireEvent('prepare', this, formData) != false){
29562             this.xhr.send(formData);
29563         };
29564     }
29565 });
29566
29567 /*
29568 * Licence: LGPL
29569 */
29570
29571 /**
29572  * @class Roo.bootstrap.DocumentViewer
29573  * @extends Roo.bootstrap.Component
29574  * Bootstrap DocumentViewer class
29575  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29576  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29577  * 
29578  * @constructor
29579  * Create a new DocumentViewer
29580  * @param {Object} config The config object
29581  */
29582
29583 Roo.bootstrap.DocumentViewer = function(config){
29584     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29585     
29586     this.addEvents({
29587         /**
29588          * @event initial
29589          * Fire after initEvent
29590          * @param {Roo.bootstrap.DocumentViewer} this
29591          */
29592         "initial" : true,
29593         /**
29594          * @event click
29595          * Fire after click
29596          * @param {Roo.bootstrap.DocumentViewer} this
29597          */
29598         "click" : true,
29599         /**
29600          * @event download
29601          * Fire after download button
29602          * @param {Roo.bootstrap.DocumentViewer} this
29603          */
29604         "download" : true,
29605         /**
29606          * @event trash
29607          * Fire after trash button
29608          * @param {Roo.bootstrap.DocumentViewer} this
29609          */
29610         "trash" : true
29611         
29612     });
29613 };
29614
29615 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29616     
29617     showDownload : true,
29618     
29619     showTrash : true,
29620     
29621     getAutoCreate : function()
29622     {
29623         var cfg = {
29624             tag : 'div',
29625             cls : 'roo-document-viewer',
29626             cn : [
29627                 {
29628                     tag : 'div',
29629                     cls : 'roo-document-viewer-body',
29630                     cn : [
29631                         {
29632                             tag : 'div',
29633                             cls : 'roo-document-viewer-thumb',
29634                             cn : [
29635                                 {
29636                                     tag : 'img',
29637                                     cls : 'roo-document-viewer-image'
29638                                 }
29639                             ]
29640                         }
29641                     ]
29642                 },
29643                 {
29644                     tag : 'div',
29645                     cls : 'roo-document-viewer-footer',
29646                     cn : {
29647                         tag : 'div',
29648                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29649                         cn : [
29650                             {
29651                                 tag : 'div',
29652                                 cls : 'btn-group roo-document-viewer-download',
29653                                 cn : [
29654                                     {
29655                                         tag : 'button',
29656                                         cls : 'btn btn-default',
29657                                         html : '<i class="fa fa-download"></i>'
29658                                     }
29659                                 ]
29660                             },
29661                             {
29662                                 tag : 'div',
29663                                 cls : 'btn-group roo-document-viewer-trash',
29664                                 cn : [
29665                                     {
29666                                         tag : 'button',
29667                                         cls : 'btn btn-default',
29668                                         html : '<i class="fa fa-trash"></i>'
29669                                     }
29670                                 ]
29671                             }
29672                         ]
29673                     }
29674                 }
29675             ]
29676         };
29677         
29678         return cfg;
29679     },
29680     
29681     initEvents : function()
29682     {
29683         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29684         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29685         
29686         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29687         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29688         
29689         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29690         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29691         
29692         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29693         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29694         
29695         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29696         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29697         
29698         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29699         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29700         
29701         this.bodyEl.on('click', this.onClick, this);
29702         this.downloadBtn.on('click', this.onDownload, this);
29703         this.trashBtn.on('click', this.onTrash, this);
29704         
29705         this.downloadBtn.hide();
29706         this.trashBtn.hide();
29707         
29708         if(this.showDownload){
29709             this.downloadBtn.show();
29710         }
29711         
29712         if(this.showTrash){
29713             this.trashBtn.show();
29714         }
29715         
29716         if(!this.showDownload && !this.showTrash) {
29717             this.footerEl.hide();
29718         }
29719         
29720     },
29721     
29722     initial : function()
29723     {
29724         this.fireEvent('initial', this);
29725         
29726     },
29727     
29728     onClick : function(e)
29729     {
29730         e.preventDefault();
29731         
29732         this.fireEvent('click', this);
29733     },
29734     
29735     onDownload : function(e)
29736     {
29737         e.preventDefault();
29738         
29739         this.fireEvent('download', this);
29740     },
29741     
29742     onTrash : function(e)
29743     {
29744         e.preventDefault();
29745         
29746         this.fireEvent('trash', this);
29747     }
29748     
29749 });
29750 /*
29751  * - LGPL
29752  *
29753  * nav progress bar
29754  * 
29755  */
29756
29757 /**
29758  * @class Roo.bootstrap.NavProgressBar
29759  * @extends Roo.bootstrap.Component
29760  * Bootstrap NavProgressBar class
29761  * 
29762  * @constructor
29763  * Create a new nav progress bar
29764  * @param {Object} config The config object
29765  */
29766
29767 Roo.bootstrap.NavProgressBar = function(config){
29768     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29769
29770     this.bullets = this.bullets || [];
29771    
29772 //    Roo.bootstrap.NavProgressBar.register(this);
29773      this.addEvents({
29774         /**
29775              * @event changed
29776              * Fires when the active item changes
29777              * @param {Roo.bootstrap.NavProgressBar} this
29778              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29779              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29780          */
29781         'changed': true
29782      });
29783     
29784 };
29785
29786 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29787     
29788     bullets : [],
29789     barItems : [],
29790     
29791     getAutoCreate : function()
29792     {
29793         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29794         
29795         cfg = {
29796             tag : 'div',
29797             cls : 'roo-navigation-bar-group',
29798             cn : [
29799                 {
29800                     tag : 'div',
29801                     cls : 'roo-navigation-top-bar'
29802                 },
29803                 {
29804                     tag : 'div',
29805                     cls : 'roo-navigation-bullets-bar',
29806                     cn : [
29807                         {
29808                             tag : 'ul',
29809                             cls : 'roo-navigation-bar'
29810                         }
29811                     ]
29812                 },
29813                 
29814                 {
29815                     tag : 'div',
29816                     cls : 'roo-navigation-bottom-bar'
29817                 }
29818             ]
29819             
29820         };
29821         
29822         return cfg;
29823         
29824     },
29825     
29826     initEvents: function() 
29827     {
29828         
29829     },
29830     
29831     onRender : function(ct, position) 
29832     {
29833         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29834         
29835         if(this.bullets.length){
29836             Roo.each(this.bullets, function(b){
29837                this.addItem(b);
29838             }, this);
29839         }
29840         
29841         this.format();
29842         
29843     },
29844     
29845     addItem : function(cfg)
29846     {
29847         var item = new Roo.bootstrap.NavProgressItem(cfg);
29848         
29849         item.parentId = this.id;
29850         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29851         
29852         if(cfg.html){
29853             var top = new Roo.bootstrap.Element({
29854                 tag : 'div',
29855                 cls : 'roo-navigation-bar-text'
29856             });
29857             
29858             var bottom = new Roo.bootstrap.Element({
29859                 tag : 'div',
29860                 cls : 'roo-navigation-bar-text'
29861             });
29862             
29863             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29864             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29865             
29866             var topText = new Roo.bootstrap.Element({
29867                 tag : 'span',
29868                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29869             });
29870             
29871             var bottomText = new Roo.bootstrap.Element({
29872                 tag : 'span',
29873                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29874             });
29875             
29876             topText.onRender(top.el, null);
29877             bottomText.onRender(bottom.el, null);
29878             
29879             item.topEl = top;
29880             item.bottomEl = bottom;
29881         }
29882         
29883         this.barItems.push(item);
29884         
29885         return item;
29886     },
29887     
29888     getActive : function()
29889     {
29890         var active = false;
29891         
29892         Roo.each(this.barItems, function(v){
29893             
29894             if (!v.isActive()) {
29895                 return;
29896             }
29897             
29898             active = v;
29899             return false;
29900             
29901         });
29902         
29903         return active;
29904     },
29905     
29906     setActiveItem : function(item)
29907     {
29908         var prev = false;
29909         
29910         Roo.each(this.barItems, function(v){
29911             if (v.rid == item.rid) {
29912                 return ;
29913             }
29914             
29915             if (v.isActive()) {
29916                 v.setActive(false);
29917                 prev = v;
29918             }
29919         });
29920
29921         item.setActive(true);
29922         
29923         this.fireEvent('changed', this, item, prev);
29924     },
29925     
29926     getBarItem: function(rid)
29927     {
29928         var ret = false;
29929         
29930         Roo.each(this.barItems, function(e) {
29931             if (e.rid != rid) {
29932                 return;
29933             }
29934             
29935             ret =  e;
29936             return false;
29937         });
29938         
29939         return ret;
29940     },
29941     
29942     indexOfItem : function(item)
29943     {
29944         var index = false;
29945         
29946         Roo.each(this.barItems, function(v, i){
29947             
29948             if (v.rid != item.rid) {
29949                 return;
29950             }
29951             
29952             index = i;
29953             return false
29954         });
29955         
29956         return index;
29957     },
29958     
29959     setActiveNext : function()
29960     {
29961         var i = this.indexOfItem(this.getActive());
29962         
29963         if (i > this.barItems.length) {
29964             return;
29965         }
29966         
29967         this.setActiveItem(this.barItems[i+1]);
29968     },
29969     
29970     setActivePrev : function()
29971     {
29972         var i = this.indexOfItem(this.getActive());
29973         
29974         if (i  < 1) {
29975             return;
29976         }
29977         
29978         this.setActiveItem(this.barItems[i-1]);
29979     },
29980     
29981     format : function()
29982     {
29983         if(!this.barItems.length){
29984             return;
29985         }
29986      
29987         var width = 100 / this.barItems.length;
29988         
29989         Roo.each(this.barItems, function(i){
29990             i.el.setStyle('width', width + '%');
29991             i.topEl.el.setStyle('width', width + '%');
29992             i.bottomEl.el.setStyle('width', width + '%');
29993         }, this);
29994         
29995     }
29996     
29997 });
29998 /*
29999  * - LGPL
30000  *
30001  * Nav Progress Item
30002  * 
30003  */
30004
30005 /**
30006  * @class Roo.bootstrap.NavProgressItem
30007  * @extends Roo.bootstrap.Component
30008  * Bootstrap NavProgressItem class
30009  * @cfg {String} rid the reference id
30010  * @cfg {Boolean} active (true|false) Is item active default false
30011  * @cfg {Boolean} disabled (true|false) Is item active default false
30012  * @cfg {String} html
30013  * @cfg {String} position (top|bottom) text position default bottom
30014  * @cfg {String} icon show icon instead of number
30015  * 
30016  * @constructor
30017  * Create a new NavProgressItem
30018  * @param {Object} config The config object
30019  */
30020 Roo.bootstrap.NavProgressItem = function(config){
30021     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30022     this.addEvents({
30023         // raw events
30024         /**
30025          * @event click
30026          * The raw click event for the entire grid.
30027          * @param {Roo.bootstrap.NavProgressItem} this
30028          * @param {Roo.EventObject} e
30029          */
30030         "click" : true
30031     });
30032    
30033 };
30034
30035 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30036     
30037     rid : '',
30038     active : false,
30039     disabled : false,
30040     html : '',
30041     position : 'bottom',
30042     icon : false,
30043     
30044     getAutoCreate : function()
30045     {
30046         var iconCls = 'roo-navigation-bar-item-icon';
30047         
30048         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30049         
30050         var cfg = {
30051             tag: 'li',
30052             cls: 'roo-navigation-bar-item',
30053             cn : [
30054                 {
30055                     tag : 'i',
30056                     cls : iconCls
30057                 }
30058             ]
30059         };
30060         
30061         if(this.active){
30062             cfg.cls += ' active';
30063         }
30064         if(this.disabled){
30065             cfg.cls += ' disabled';
30066         }
30067         
30068         return cfg;
30069     },
30070     
30071     disable : function()
30072     {
30073         this.setDisabled(true);
30074     },
30075     
30076     enable : function()
30077     {
30078         this.setDisabled(false);
30079     },
30080     
30081     initEvents: function() 
30082     {
30083         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30084         
30085         this.iconEl.on('click', this.onClick, this);
30086     },
30087     
30088     onClick : function(e)
30089     {
30090         e.preventDefault();
30091         
30092         if(this.disabled){
30093             return;
30094         }
30095         
30096         if(this.fireEvent('click', this, e) === false){
30097             return;
30098         };
30099         
30100         this.parent().setActiveItem(this);
30101     },
30102     
30103     isActive: function () 
30104     {
30105         return this.active;
30106     },
30107     
30108     setActive : function(state)
30109     {
30110         if(this.active == state){
30111             return;
30112         }
30113         
30114         this.active = state;
30115         
30116         if (state) {
30117             this.el.addClass('active');
30118             return;
30119         }
30120         
30121         this.el.removeClass('active');
30122         
30123         return;
30124     },
30125     
30126     setDisabled : function(state)
30127     {
30128         if(this.disabled == state){
30129             return;
30130         }
30131         
30132         this.disabled = state;
30133         
30134         if (state) {
30135             this.el.addClass('disabled');
30136             return;
30137         }
30138         
30139         this.el.removeClass('disabled');
30140     },
30141     
30142     tooltipEl : function()
30143     {
30144         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30145     }
30146 });
30147  
30148
30149  /*
30150  * - LGPL
30151  *
30152  * FieldLabel
30153  * 
30154  */
30155
30156 /**
30157  * @class Roo.bootstrap.FieldLabel
30158  * @extends Roo.bootstrap.Component
30159  * Bootstrap FieldLabel class
30160  * @cfg {String} html contents of the element
30161  * @cfg {String} tag tag of the element default label
30162  * @cfg {String} cls class of the element
30163  * @cfg {String} target label target 
30164  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30165  * @cfg {String} invalidClass default "text-warning"
30166  * @cfg {String} validClass default "text-success"
30167  * @cfg {String} iconTooltip default "This field is required"
30168  * @cfg {String} indicatorpos (left|right) default left
30169  * 
30170  * @constructor
30171  * Create a new FieldLabel
30172  * @param {Object} config The config object
30173  */
30174
30175 Roo.bootstrap.FieldLabel = function(config){
30176     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30177     
30178     this.addEvents({
30179             /**
30180              * @event invalid
30181              * Fires after the field has been marked as invalid.
30182              * @param {Roo.form.FieldLabel} this
30183              * @param {String} msg The validation message
30184              */
30185             invalid : true,
30186             /**
30187              * @event valid
30188              * Fires after the field has been validated with no errors.
30189              * @param {Roo.form.FieldLabel} this
30190              */
30191             valid : true
30192         });
30193 };
30194
30195 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30196     
30197     tag: 'label',
30198     cls: '',
30199     html: '',
30200     target: '',
30201     allowBlank : true,
30202     invalidClass : 'has-warning',
30203     validClass : 'has-success',
30204     iconTooltip : 'This field is required',
30205     indicatorpos : 'left',
30206     
30207     getAutoCreate : function(){
30208         
30209         var cls = "";
30210         if (!this.allowBlank) {
30211             cls  = "visible";
30212         }
30213         
30214         var cfg = {
30215             tag : this.tag,
30216             cls : 'roo-bootstrap-field-label ' + this.cls,
30217             for : this.target,
30218             cn : [
30219                 {
30220                     tag : 'i',
30221                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30222                     tooltip : this.iconTooltip
30223                 },
30224                 {
30225                     tag : 'span',
30226                     html : this.html
30227                 }
30228             ] 
30229         };
30230         
30231         if(this.indicatorpos == 'right'){
30232             var cfg = {
30233                 tag : this.tag,
30234                 cls : 'roo-bootstrap-field-label ' + this.cls,
30235                 for : this.target,
30236                 cn : [
30237                     {
30238                         tag : 'span',
30239                         html : this.html
30240                     },
30241                     {
30242                         tag : 'i',
30243                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30244                         tooltip : this.iconTooltip
30245                     }
30246                 ] 
30247             };
30248         }
30249         
30250         return cfg;
30251     },
30252     
30253     initEvents: function() 
30254     {
30255         Roo.bootstrap.Element.superclass.initEvents.call(this);
30256         
30257         this.indicator = this.indicatorEl();
30258         
30259         if(this.indicator){
30260             this.indicator.removeClass('visible');
30261             this.indicator.addClass('invisible');
30262         }
30263         
30264         Roo.bootstrap.FieldLabel.register(this);
30265     },
30266     
30267     indicatorEl : function()
30268     {
30269         var indicator = this.el.select('i.roo-required-indicator',true).first();
30270         
30271         if(!indicator){
30272             return false;
30273         }
30274         
30275         return indicator;
30276         
30277     },
30278     
30279     /**
30280      * Mark this field as valid
30281      */
30282     markValid : function()
30283     {
30284         if(this.indicator){
30285             this.indicator.removeClass('visible');
30286             this.indicator.addClass('invisible');
30287         }
30288         
30289         this.el.removeClass(this.invalidClass);
30290         
30291         this.el.addClass(this.validClass);
30292         
30293         this.fireEvent('valid', this);
30294     },
30295     
30296     /**
30297      * Mark this field as invalid
30298      * @param {String} msg The validation message
30299      */
30300     markInvalid : function(msg)
30301     {
30302         if(this.indicator){
30303             this.indicator.removeClass('invisible');
30304             this.indicator.addClass('visible');
30305         }
30306         
30307         this.el.removeClass(this.validClass);
30308         
30309         this.el.addClass(this.invalidClass);
30310         
30311         this.fireEvent('invalid', this, msg);
30312     }
30313     
30314    
30315 });
30316
30317 Roo.apply(Roo.bootstrap.FieldLabel, {
30318     
30319     groups: {},
30320     
30321      /**
30322     * register a FieldLabel Group
30323     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30324     */
30325     register : function(label)
30326     {
30327         if(this.groups.hasOwnProperty(label.target)){
30328             return;
30329         }
30330      
30331         this.groups[label.target] = label;
30332         
30333     },
30334     /**
30335     * fetch a FieldLabel Group based on the target
30336     * @param {string} target
30337     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30338     */
30339     get: function(target) {
30340         if (typeof(this.groups[target]) == 'undefined') {
30341             return false;
30342         }
30343         
30344         return this.groups[target] ;
30345     }
30346 });
30347
30348  
30349
30350  /*
30351  * - LGPL
30352  *
30353  * page DateSplitField.
30354  * 
30355  */
30356
30357
30358 /**
30359  * @class Roo.bootstrap.DateSplitField
30360  * @extends Roo.bootstrap.Component
30361  * Bootstrap DateSplitField class
30362  * @cfg {string} fieldLabel - the label associated
30363  * @cfg {Number} labelWidth set the width of label (0-12)
30364  * @cfg {String} labelAlign (top|left)
30365  * @cfg {Boolean} dayAllowBlank (true|false) default false
30366  * @cfg {Boolean} monthAllowBlank (true|false) default false
30367  * @cfg {Boolean} yearAllowBlank (true|false) default false
30368  * @cfg {string} dayPlaceholder 
30369  * @cfg {string} monthPlaceholder
30370  * @cfg {string} yearPlaceholder
30371  * @cfg {string} dayFormat default 'd'
30372  * @cfg {string} monthFormat default 'm'
30373  * @cfg {string} yearFormat default 'Y'
30374  * @cfg {Number} labellg set the width of label (1-12)
30375  * @cfg {Number} labelmd set the width of label (1-12)
30376  * @cfg {Number} labelsm set the width of label (1-12)
30377  * @cfg {Number} labelxs set the width of label (1-12)
30378
30379  *     
30380  * @constructor
30381  * Create a new DateSplitField
30382  * @param {Object} config The config object
30383  */
30384
30385 Roo.bootstrap.DateSplitField = function(config){
30386     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30387     
30388     this.addEvents({
30389         // raw events
30390          /**
30391          * @event years
30392          * getting the data of years
30393          * @param {Roo.bootstrap.DateSplitField} this
30394          * @param {Object} years
30395          */
30396         "years" : true,
30397         /**
30398          * @event days
30399          * getting the data of days
30400          * @param {Roo.bootstrap.DateSplitField} this
30401          * @param {Object} days
30402          */
30403         "days" : true,
30404         /**
30405          * @event invalid
30406          * Fires after the field has been marked as invalid.
30407          * @param {Roo.form.Field} this
30408          * @param {String} msg The validation message
30409          */
30410         invalid : true,
30411        /**
30412          * @event valid
30413          * Fires after the field has been validated with no errors.
30414          * @param {Roo.form.Field} this
30415          */
30416         valid : true
30417     });
30418 };
30419
30420 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30421     
30422     fieldLabel : '',
30423     labelAlign : 'top',
30424     labelWidth : 3,
30425     dayAllowBlank : false,
30426     monthAllowBlank : false,
30427     yearAllowBlank : false,
30428     dayPlaceholder : '',
30429     monthPlaceholder : '',
30430     yearPlaceholder : '',
30431     dayFormat : 'd',
30432     monthFormat : 'm',
30433     yearFormat : 'Y',
30434     isFormField : true,
30435     labellg : 0,
30436     labelmd : 0,
30437     labelsm : 0,
30438     labelxs : 0,
30439     
30440     getAutoCreate : function()
30441     {
30442         var cfg = {
30443             tag : 'div',
30444             cls : 'row roo-date-split-field-group',
30445             cn : [
30446                 {
30447                     tag : 'input',
30448                     type : 'hidden',
30449                     cls : 'form-hidden-field roo-date-split-field-group-value',
30450                     name : this.name
30451                 }
30452             ]
30453         };
30454         
30455         var labelCls = 'col-md-12';
30456         var contentCls = 'col-md-4';
30457         
30458         if(this.fieldLabel){
30459             
30460             var label = {
30461                 tag : 'div',
30462                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30463                 cn : [
30464                     {
30465                         tag : 'label',
30466                         html : this.fieldLabel
30467                     }
30468                 ]
30469             };
30470             
30471             if(this.labelAlign == 'left'){
30472             
30473                 if(this.labelWidth > 12){
30474                     label.style = "width: " + this.labelWidth + 'px';
30475                 }
30476
30477                 if(this.labelWidth < 13 && this.labelmd == 0){
30478                     this.labelmd = this.labelWidth;
30479                 }
30480
30481                 if(this.labellg > 0){
30482                     labelCls = ' col-lg-' + this.labellg;
30483                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30484                 }
30485
30486                 if(this.labelmd > 0){
30487                     labelCls = ' col-md-' + this.labelmd;
30488                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30489                 }
30490
30491                 if(this.labelsm > 0){
30492                     labelCls = ' col-sm-' + this.labelsm;
30493                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30494                 }
30495
30496                 if(this.labelxs > 0){
30497                     labelCls = ' col-xs-' + this.labelxs;
30498                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30499                 }
30500             }
30501             
30502             label.cls += ' ' + labelCls;
30503             
30504             cfg.cn.push(label);
30505         }
30506         
30507         Roo.each(['day', 'month', 'year'], function(t){
30508             cfg.cn.push({
30509                 tag : 'div',
30510                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30511             });
30512         }, this);
30513         
30514         return cfg;
30515     },
30516     
30517     inputEl: function ()
30518     {
30519         return this.el.select('.roo-date-split-field-group-value', true).first();
30520     },
30521     
30522     onRender : function(ct, position) 
30523     {
30524         var _this = this;
30525         
30526         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30527         
30528         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30529         
30530         this.dayField = new Roo.bootstrap.ComboBox({
30531             allowBlank : this.dayAllowBlank,
30532             alwaysQuery : true,
30533             displayField : 'value',
30534             editable : false,
30535             fieldLabel : '',
30536             forceSelection : true,
30537             mode : 'local',
30538             placeholder : this.dayPlaceholder,
30539             selectOnFocus : true,
30540             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30541             triggerAction : 'all',
30542             typeAhead : true,
30543             valueField : 'value',
30544             store : new Roo.data.SimpleStore({
30545                 data : (function() {    
30546                     var days = [];
30547                     _this.fireEvent('days', _this, days);
30548                     return days;
30549                 })(),
30550                 fields : [ 'value' ]
30551             }),
30552             listeners : {
30553                 select : function (_self, record, index)
30554                 {
30555                     _this.setValue(_this.getValue());
30556                 }
30557             }
30558         });
30559
30560         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30561         
30562         this.monthField = new Roo.bootstrap.MonthField({
30563             after : '<i class=\"fa fa-calendar\"></i>',
30564             allowBlank : this.monthAllowBlank,
30565             placeholder : this.monthPlaceholder,
30566             readOnly : true,
30567             listeners : {
30568                 render : function (_self)
30569                 {
30570                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30571                         e.preventDefault();
30572                         _self.focus();
30573                     });
30574                 },
30575                 select : function (_self, oldvalue, newvalue)
30576                 {
30577                     _this.setValue(_this.getValue());
30578                 }
30579             }
30580         });
30581         
30582         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30583         
30584         this.yearField = new Roo.bootstrap.ComboBox({
30585             allowBlank : this.yearAllowBlank,
30586             alwaysQuery : true,
30587             displayField : 'value',
30588             editable : false,
30589             fieldLabel : '',
30590             forceSelection : true,
30591             mode : 'local',
30592             placeholder : this.yearPlaceholder,
30593             selectOnFocus : true,
30594             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30595             triggerAction : 'all',
30596             typeAhead : true,
30597             valueField : 'value',
30598             store : new Roo.data.SimpleStore({
30599                 data : (function() {
30600                     var years = [];
30601                     _this.fireEvent('years', _this, years);
30602                     return years;
30603                 })(),
30604                 fields : [ 'value' ]
30605             }),
30606             listeners : {
30607                 select : function (_self, record, index)
30608                 {
30609                     _this.setValue(_this.getValue());
30610                 }
30611             }
30612         });
30613
30614         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30615     },
30616     
30617     setValue : function(v, format)
30618     {
30619         this.inputEl.dom.value = v;
30620         
30621         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30622         
30623         var d = Date.parseDate(v, f);
30624         
30625         if(!d){
30626             this.validate();
30627             return;
30628         }
30629         
30630         this.setDay(d.format(this.dayFormat));
30631         this.setMonth(d.format(this.monthFormat));
30632         this.setYear(d.format(this.yearFormat));
30633         
30634         this.validate();
30635         
30636         return;
30637     },
30638     
30639     setDay : function(v)
30640     {
30641         this.dayField.setValue(v);
30642         this.inputEl.dom.value = this.getValue();
30643         this.validate();
30644         return;
30645     },
30646     
30647     setMonth : function(v)
30648     {
30649         this.monthField.setValue(v, true);
30650         this.inputEl.dom.value = this.getValue();
30651         this.validate();
30652         return;
30653     },
30654     
30655     setYear : function(v)
30656     {
30657         this.yearField.setValue(v);
30658         this.inputEl.dom.value = this.getValue();
30659         this.validate();
30660         return;
30661     },
30662     
30663     getDay : function()
30664     {
30665         return this.dayField.getValue();
30666     },
30667     
30668     getMonth : function()
30669     {
30670         return this.monthField.getValue();
30671     },
30672     
30673     getYear : function()
30674     {
30675         return this.yearField.getValue();
30676     },
30677     
30678     getValue : function()
30679     {
30680         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30681         
30682         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30683         
30684         return date;
30685     },
30686     
30687     reset : function()
30688     {
30689         this.setDay('');
30690         this.setMonth('');
30691         this.setYear('');
30692         this.inputEl.dom.value = '';
30693         this.validate();
30694         return;
30695     },
30696     
30697     validate : function()
30698     {
30699         var d = this.dayField.validate();
30700         var m = this.monthField.validate();
30701         var y = this.yearField.validate();
30702         
30703         var valid = true;
30704         
30705         if(
30706                 (!this.dayAllowBlank && !d) ||
30707                 (!this.monthAllowBlank && !m) ||
30708                 (!this.yearAllowBlank && !y)
30709         ){
30710             valid = false;
30711         }
30712         
30713         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30714             return valid;
30715         }
30716         
30717         if(valid){
30718             this.markValid();
30719             return valid;
30720         }
30721         
30722         this.markInvalid();
30723         
30724         return valid;
30725     },
30726     
30727     markValid : function()
30728     {
30729         
30730         var label = this.el.select('label', true).first();
30731         var icon = this.el.select('i.fa-star', true).first();
30732
30733         if(label && icon){
30734             icon.remove();
30735         }
30736         
30737         this.fireEvent('valid', this);
30738     },
30739     
30740      /**
30741      * Mark this field as invalid
30742      * @param {String} msg The validation message
30743      */
30744     markInvalid : function(msg)
30745     {
30746         
30747         var label = this.el.select('label', true).first();
30748         var icon = this.el.select('i.fa-star', true).first();
30749
30750         if(label && !icon){
30751             this.el.select('.roo-date-split-field-label', true).createChild({
30752                 tag : 'i',
30753                 cls : 'text-danger fa fa-lg fa-star',
30754                 tooltip : 'This field is required',
30755                 style : 'margin-right:5px;'
30756             }, label, true);
30757         }
30758         
30759         this.fireEvent('invalid', this, msg);
30760     },
30761     
30762     clearInvalid : function()
30763     {
30764         var label = this.el.select('label', true).first();
30765         var icon = this.el.select('i.fa-star', true).first();
30766
30767         if(label && icon){
30768             icon.remove();
30769         }
30770         
30771         this.fireEvent('valid', this);
30772     },
30773     
30774     getName: function()
30775     {
30776         return this.name;
30777     }
30778     
30779 });
30780
30781  /**
30782  *
30783  * This is based on 
30784  * http://masonry.desandro.com
30785  *
30786  * The idea is to render all the bricks based on vertical width...
30787  *
30788  * The original code extends 'outlayer' - we might need to use that....
30789  * 
30790  */
30791
30792
30793 /**
30794  * @class Roo.bootstrap.LayoutMasonry
30795  * @extends Roo.bootstrap.Component
30796  * Bootstrap Layout Masonry class
30797  * 
30798  * @constructor
30799  * Create a new Element
30800  * @param {Object} config The config object
30801  */
30802
30803 Roo.bootstrap.LayoutMasonry = function(config){
30804     
30805     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30806     
30807     this.bricks = [];
30808     
30809     Roo.bootstrap.LayoutMasonry.register(this);
30810     
30811     this.addEvents({
30812         // raw events
30813         /**
30814          * @event layout
30815          * Fire after layout the items
30816          * @param {Roo.bootstrap.LayoutMasonry} this
30817          * @param {Roo.EventObject} e
30818          */
30819         "layout" : true
30820     });
30821     
30822 };
30823
30824 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30825     
30826     /**
30827      * @cfg {Boolean} isLayoutInstant = no animation?
30828      */   
30829     isLayoutInstant : false, // needed?
30830    
30831     /**
30832      * @cfg {Number} boxWidth  width of the columns
30833      */   
30834     boxWidth : 450,
30835     
30836       /**
30837      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30838      */   
30839     boxHeight : 0,
30840     
30841     /**
30842      * @cfg {Number} padWidth padding below box..
30843      */   
30844     padWidth : 10, 
30845     
30846     /**
30847      * @cfg {Number} gutter gutter width..
30848      */   
30849     gutter : 10,
30850     
30851      /**
30852      * @cfg {Number} maxCols maximum number of columns
30853      */   
30854     
30855     maxCols: 0,
30856     
30857     /**
30858      * @cfg {Boolean} isAutoInitial defalut true
30859      */   
30860     isAutoInitial : true, 
30861     
30862     containerWidth: 0,
30863     
30864     /**
30865      * @cfg {Boolean} isHorizontal defalut false
30866      */   
30867     isHorizontal : false, 
30868
30869     currentSize : null,
30870     
30871     tag: 'div',
30872     
30873     cls: '',
30874     
30875     bricks: null, //CompositeElement
30876     
30877     cols : 1,
30878     
30879     _isLayoutInited : false,
30880     
30881 //    isAlternative : false, // only use for vertical layout...
30882     
30883     /**
30884      * @cfg {Number} alternativePadWidth padding below box..
30885      */   
30886     alternativePadWidth : 50,
30887     
30888     selectedBrick : [],
30889     
30890     getAutoCreate : function(){
30891         
30892         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30893         
30894         var cfg = {
30895             tag: this.tag,
30896             cls: 'blog-masonary-wrapper ' + this.cls,
30897             cn : {
30898                 cls : 'mas-boxes masonary'
30899             }
30900         };
30901         
30902         return cfg;
30903     },
30904     
30905     getChildContainer: function( )
30906     {
30907         if (this.boxesEl) {
30908             return this.boxesEl;
30909         }
30910         
30911         this.boxesEl = this.el.select('.mas-boxes').first();
30912         
30913         return this.boxesEl;
30914     },
30915     
30916     
30917     initEvents : function()
30918     {
30919         var _this = this;
30920         
30921         if(this.isAutoInitial){
30922             Roo.log('hook children rendered');
30923             this.on('childrenrendered', function() {
30924                 Roo.log('children rendered');
30925                 _this.initial();
30926             } ,this);
30927         }
30928     },
30929     
30930     initial : function()
30931     {
30932         this.selectedBrick = [];
30933         
30934         this.currentSize = this.el.getBox(true);
30935         
30936         Roo.EventManager.onWindowResize(this.resize, this); 
30937
30938         if(!this.isAutoInitial){
30939             this.layout();
30940             return;
30941         }
30942         
30943         this.layout();
30944         
30945         return;
30946         //this.layout.defer(500,this);
30947         
30948     },
30949     
30950     resize : function()
30951     {
30952         var cs = this.el.getBox(true);
30953         
30954         if (
30955                 this.currentSize.width == cs.width && 
30956                 this.currentSize.x == cs.x && 
30957                 this.currentSize.height == cs.height && 
30958                 this.currentSize.y == cs.y 
30959         ) {
30960             Roo.log("no change in with or X or Y");
30961             return;
30962         }
30963         
30964         this.currentSize = cs;
30965         
30966         this.layout();
30967         
30968     },
30969     
30970     layout : function()
30971     {   
30972         this._resetLayout();
30973         
30974         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30975         
30976         this.layoutItems( isInstant );
30977       
30978         this._isLayoutInited = true;
30979         
30980         this.fireEvent('layout', this);
30981         
30982     },
30983     
30984     _resetLayout : function()
30985     {
30986         if(this.isHorizontal){
30987             this.horizontalMeasureColumns();
30988             return;
30989         }
30990         
30991         this.verticalMeasureColumns();
30992         
30993     },
30994     
30995     verticalMeasureColumns : function()
30996     {
30997         this.getContainerWidth();
30998         
30999 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31000 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31001 //            return;
31002 //        }
31003         
31004         var boxWidth = this.boxWidth + this.padWidth;
31005         
31006         if(this.containerWidth < this.boxWidth){
31007             boxWidth = this.containerWidth
31008         }
31009         
31010         var containerWidth = this.containerWidth;
31011         
31012         var cols = Math.floor(containerWidth / boxWidth);
31013         
31014         this.cols = Math.max( cols, 1 );
31015         
31016         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31017         
31018         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31019         
31020         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31021         
31022         this.colWidth = boxWidth + avail - this.padWidth;
31023         
31024         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31025         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31026     },
31027     
31028     horizontalMeasureColumns : function()
31029     {
31030         this.getContainerWidth();
31031         
31032         var boxWidth = this.boxWidth;
31033         
31034         if(this.containerWidth < boxWidth){
31035             boxWidth = this.containerWidth;
31036         }
31037         
31038         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31039         
31040         this.el.setHeight(boxWidth);
31041         
31042     },
31043     
31044     getContainerWidth : function()
31045     {
31046         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31047     },
31048     
31049     layoutItems : function( isInstant )
31050     {
31051         Roo.log(this.bricks);
31052         
31053         var items = Roo.apply([], this.bricks);
31054         
31055         if(this.isHorizontal){
31056             this._horizontalLayoutItems( items , isInstant );
31057             return;
31058         }
31059         
31060 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31061 //            this._verticalAlternativeLayoutItems( items , isInstant );
31062 //            return;
31063 //        }
31064         
31065         this._verticalLayoutItems( items , isInstant );
31066         
31067     },
31068     
31069     _verticalLayoutItems : function ( items , isInstant)
31070     {
31071         if ( !items || !items.length ) {
31072             return;
31073         }
31074         
31075         var standard = [
31076             ['xs', 'xs', 'xs', 'tall'],
31077             ['xs', 'xs', 'tall'],
31078             ['xs', 'xs', 'sm'],
31079             ['xs', 'xs', 'xs'],
31080             ['xs', 'tall'],
31081             ['xs', 'sm'],
31082             ['xs', 'xs'],
31083             ['xs'],
31084             
31085             ['sm', 'xs', 'xs'],
31086             ['sm', 'xs'],
31087             ['sm'],
31088             
31089             ['tall', 'xs', 'xs', 'xs'],
31090             ['tall', 'xs', 'xs'],
31091             ['tall', 'xs'],
31092             ['tall']
31093             
31094         ];
31095         
31096         var queue = [];
31097         
31098         var boxes = [];
31099         
31100         var box = [];
31101         
31102         Roo.each(items, function(item, k){
31103             
31104             switch (item.size) {
31105                 // these layouts take up a full box,
31106                 case 'md' :
31107                 case 'md-left' :
31108                 case 'md-right' :
31109                 case 'wide' :
31110                     
31111                     if(box.length){
31112                         boxes.push(box);
31113                         box = [];
31114                     }
31115                     
31116                     boxes.push([item]);
31117                     
31118                     break;
31119                     
31120                 case 'xs' :
31121                 case 'sm' :
31122                 case 'tall' :
31123                     
31124                     box.push(item);
31125                     
31126                     break;
31127                 default :
31128                     break;
31129                     
31130             }
31131             
31132         }, this);
31133         
31134         if(box.length){
31135             boxes.push(box);
31136             box = [];
31137         }
31138         
31139         var filterPattern = function(box, length)
31140         {
31141             if(!box.length){
31142                 return;
31143             }
31144             
31145             var match = false;
31146             
31147             var pattern = box.slice(0, length);
31148             
31149             var format = [];
31150             
31151             Roo.each(pattern, function(i){
31152                 format.push(i.size);
31153             }, this);
31154             
31155             Roo.each(standard, function(s){
31156                 
31157                 if(String(s) != String(format)){
31158                     return;
31159                 }
31160                 
31161                 match = true;
31162                 return false;
31163                 
31164             }, this);
31165             
31166             if(!match && length == 1){
31167                 return;
31168             }
31169             
31170             if(!match){
31171                 filterPattern(box, length - 1);
31172                 return;
31173             }
31174                 
31175             queue.push(pattern);
31176
31177             box = box.slice(length, box.length);
31178
31179             filterPattern(box, 4);
31180
31181             return;
31182             
31183         }
31184         
31185         Roo.each(boxes, function(box, k){
31186             
31187             if(!box.length){
31188                 return;
31189             }
31190             
31191             if(box.length == 1){
31192                 queue.push(box);
31193                 return;
31194             }
31195             
31196             filterPattern(box, 4);
31197             
31198         }, this);
31199         
31200         this._processVerticalLayoutQueue( queue, isInstant );
31201         
31202     },
31203     
31204 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31205 //    {
31206 //        if ( !items || !items.length ) {
31207 //            return;
31208 //        }
31209 //
31210 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31211 //        
31212 //    },
31213     
31214     _horizontalLayoutItems : function ( items , isInstant)
31215     {
31216         if ( !items || !items.length || items.length < 3) {
31217             return;
31218         }
31219         
31220         items.reverse();
31221         
31222         var eItems = items.slice(0, 3);
31223         
31224         items = items.slice(3, items.length);
31225         
31226         var standard = [
31227             ['xs', 'xs', 'xs', 'wide'],
31228             ['xs', 'xs', 'wide'],
31229             ['xs', 'xs', 'sm'],
31230             ['xs', 'xs', 'xs'],
31231             ['xs', 'wide'],
31232             ['xs', 'sm'],
31233             ['xs', 'xs'],
31234             ['xs'],
31235             
31236             ['sm', 'xs', 'xs'],
31237             ['sm', 'xs'],
31238             ['sm'],
31239             
31240             ['wide', 'xs', 'xs', 'xs'],
31241             ['wide', 'xs', 'xs'],
31242             ['wide', 'xs'],
31243             ['wide'],
31244             
31245             ['wide-thin']
31246         ];
31247         
31248         var queue = [];
31249         
31250         var boxes = [];
31251         
31252         var box = [];
31253         
31254         Roo.each(items, function(item, k){
31255             
31256             switch (item.size) {
31257                 case 'md' :
31258                 case 'md-left' :
31259                 case 'md-right' :
31260                 case 'tall' :
31261                     
31262                     if(box.length){
31263                         boxes.push(box);
31264                         box = [];
31265                     }
31266                     
31267                     boxes.push([item]);
31268                     
31269                     break;
31270                     
31271                 case 'xs' :
31272                 case 'sm' :
31273                 case 'wide' :
31274                 case 'wide-thin' :
31275                     
31276                     box.push(item);
31277                     
31278                     break;
31279                 default :
31280                     break;
31281                     
31282             }
31283             
31284         }, this);
31285         
31286         if(box.length){
31287             boxes.push(box);
31288             box = [];
31289         }
31290         
31291         var filterPattern = function(box, length)
31292         {
31293             if(!box.length){
31294                 return;
31295             }
31296             
31297             var match = false;
31298             
31299             var pattern = box.slice(0, length);
31300             
31301             var format = [];
31302             
31303             Roo.each(pattern, function(i){
31304                 format.push(i.size);
31305             }, this);
31306             
31307             Roo.each(standard, function(s){
31308                 
31309                 if(String(s) != String(format)){
31310                     return;
31311                 }
31312                 
31313                 match = true;
31314                 return false;
31315                 
31316             }, this);
31317             
31318             if(!match && length == 1){
31319                 return;
31320             }
31321             
31322             if(!match){
31323                 filterPattern(box, length - 1);
31324                 return;
31325             }
31326                 
31327             queue.push(pattern);
31328
31329             box = box.slice(length, box.length);
31330
31331             filterPattern(box, 4);
31332
31333             return;
31334             
31335         }
31336         
31337         Roo.each(boxes, function(box, k){
31338             
31339             if(!box.length){
31340                 return;
31341             }
31342             
31343             if(box.length == 1){
31344                 queue.push(box);
31345                 return;
31346             }
31347             
31348             filterPattern(box, 4);
31349             
31350         }, this);
31351         
31352         
31353         var prune = [];
31354         
31355         var pos = this.el.getBox(true);
31356         
31357         var minX = pos.x;
31358         
31359         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31360         
31361         var hit_end = false;
31362         
31363         Roo.each(queue, function(box){
31364             
31365             if(hit_end){
31366                 
31367                 Roo.each(box, function(b){
31368                 
31369                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31370                     b.el.hide();
31371
31372                 }, this);
31373
31374                 return;
31375             }
31376             
31377             var mx = 0;
31378             
31379             Roo.each(box, function(b){
31380                 
31381                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31382                 b.el.show();
31383
31384                 mx = Math.max(mx, b.x);
31385                 
31386             }, this);
31387             
31388             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31389             
31390             if(maxX < minX){
31391                 
31392                 Roo.each(box, function(b){
31393                 
31394                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31395                     b.el.hide();
31396                     
31397                 }, this);
31398                 
31399                 hit_end = true;
31400                 
31401                 return;
31402             }
31403             
31404             prune.push(box);
31405             
31406         }, this);
31407         
31408         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31409     },
31410     
31411     /** Sets position of item in DOM
31412     * @param {Element} item
31413     * @param {Number} x - horizontal position
31414     * @param {Number} y - vertical position
31415     * @param {Boolean} isInstant - disables transitions
31416     */
31417     _processVerticalLayoutQueue : function( queue, isInstant )
31418     {
31419         var pos = this.el.getBox(true);
31420         var x = pos.x;
31421         var y = pos.y;
31422         var maxY = [];
31423         
31424         for (var i = 0; i < this.cols; i++){
31425             maxY[i] = pos.y;
31426         }
31427         
31428         Roo.each(queue, function(box, k){
31429             
31430             var col = k % this.cols;
31431             
31432             Roo.each(box, function(b,kk){
31433                 
31434                 b.el.position('absolute');
31435                 
31436                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31437                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31438                 
31439                 if(b.size == 'md-left' || b.size == 'md-right'){
31440                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31441                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31442                 }
31443                 
31444                 b.el.setWidth(width);
31445                 b.el.setHeight(height);
31446                 // iframe?
31447                 b.el.select('iframe',true).setSize(width,height);
31448                 
31449             }, this);
31450             
31451             for (var i = 0; i < this.cols; i++){
31452                 
31453                 if(maxY[i] < maxY[col]){
31454                     col = i;
31455                     continue;
31456                 }
31457                 
31458                 col = Math.min(col, i);
31459                 
31460             }
31461             
31462             x = pos.x + col * (this.colWidth + this.padWidth);
31463             
31464             y = maxY[col];
31465             
31466             var positions = [];
31467             
31468             switch (box.length){
31469                 case 1 :
31470                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31471                     break;
31472                 case 2 :
31473                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31474                     break;
31475                 case 3 :
31476                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31477                     break;
31478                 case 4 :
31479                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31480                     break;
31481                 default :
31482                     break;
31483             }
31484             
31485             Roo.each(box, function(b,kk){
31486                 
31487                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31488                 
31489                 var sz = b.el.getSize();
31490                 
31491                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31492                 
31493             }, this);
31494             
31495         }, this);
31496         
31497         var mY = 0;
31498         
31499         for (var i = 0; i < this.cols; i++){
31500             mY = Math.max(mY, maxY[i]);
31501         }
31502         
31503         this.el.setHeight(mY - pos.y);
31504         
31505     },
31506     
31507 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31508 //    {
31509 //        var pos = this.el.getBox(true);
31510 //        var x = pos.x;
31511 //        var y = pos.y;
31512 //        var maxX = pos.right;
31513 //        
31514 //        var maxHeight = 0;
31515 //        
31516 //        Roo.each(items, function(item, k){
31517 //            
31518 //            var c = k % 2;
31519 //            
31520 //            item.el.position('absolute');
31521 //                
31522 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31523 //
31524 //            item.el.setWidth(width);
31525 //
31526 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31527 //
31528 //            item.el.setHeight(height);
31529 //            
31530 //            if(c == 0){
31531 //                item.el.setXY([x, y], isInstant ? false : true);
31532 //            } else {
31533 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31534 //            }
31535 //            
31536 //            y = y + height + this.alternativePadWidth;
31537 //            
31538 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31539 //            
31540 //        }, this);
31541 //        
31542 //        this.el.setHeight(maxHeight);
31543 //        
31544 //    },
31545     
31546     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31547     {
31548         var pos = this.el.getBox(true);
31549         
31550         var minX = pos.x;
31551         var minY = pos.y;
31552         
31553         var maxX = pos.right;
31554         
31555         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31556         
31557         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31558         
31559         Roo.each(queue, function(box, k){
31560             
31561             Roo.each(box, function(b, kk){
31562                 
31563                 b.el.position('absolute');
31564                 
31565                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31566                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31567                 
31568                 if(b.size == 'md-left' || b.size == 'md-right'){
31569                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31570                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31571                 }
31572                 
31573                 b.el.setWidth(width);
31574                 b.el.setHeight(height);
31575                 
31576             }, this);
31577             
31578             if(!box.length){
31579                 return;
31580             }
31581             
31582             var positions = [];
31583             
31584             switch (box.length){
31585                 case 1 :
31586                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31587                     break;
31588                 case 2 :
31589                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31590                     break;
31591                 case 3 :
31592                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31593                     break;
31594                 case 4 :
31595                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31596                     break;
31597                 default :
31598                     break;
31599             }
31600             
31601             Roo.each(box, function(b,kk){
31602                 
31603                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31604                 
31605                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31606                 
31607             }, this);
31608             
31609         }, this);
31610         
31611     },
31612     
31613     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31614     {
31615         Roo.each(eItems, function(b,k){
31616             
31617             b.size = (k == 0) ? 'sm' : 'xs';
31618             b.x = (k == 0) ? 2 : 1;
31619             b.y = (k == 0) ? 2 : 1;
31620             
31621             b.el.position('absolute');
31622             
31623             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31624                 
31625             b.el.setWidth(width);
31626             
31627             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31628             
31629             b.el.setHeight(height);
31630             
31631         }, this);
31632
31633         var positions = [];
31634         
31635         positions.push({
31636             x : maxX - this.unitWidth * 2 - this.gutter,
31637             y : minY
31638         });
31639         
31640         positions.push({
31641             x : maxX - this.unitWidth,
31642             y : minY + (this.unitWidth + this.gutter) * 2
31643         });
31644         
31645         positions.push({
31646             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31647             y : minY
31648         });
31649         
31650         Roo.each(eItems, function(b,k){
31651             
31652             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31653
31654         }, this);
31655         
31656     },
31657     
31658     getVerticalOneBoxColPositions : function(x, y, box)
31659     {
31660         var pos = [];
31661         
31662         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31663         
31664         if(box[0].size == 'md-left'){
31665             rand = 0;
31666         }
31667         
31668         if(box[0].size == 'md-right'){
31669             rand = 1;
31670         }
31671         
31672         pos.push({
31673             x : x + (this.unitWidth + this.gutter) * rand,
31674             y : y
31675         });
31676         
31677         return pos;
31678     },
31679     
31680     getVerticalTwoBoxColPositions : function(x, y, box)
31681     {
31682         var pos = [];
31683         
31684         if(box[0].size == 'xs'){
31685             
31686             pos.push({
31687                 x : x,
31688                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31689             });
31690
31691             pos.push({
31692                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31693                 y : y
31694             });
31695             
31696             return pos;
31697             
31698         }
31699         
31700         pos.push({
31701             x : x,
31702             y : y
31703         });
31704
31705         pos.push({
31706             x : x + (this.unitWidth + this.gutter) * 2,
31707             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31708         });
31709         
31710         return pos;
31711         
31712     },
31713     
31714     getVerticalThreeBoxColPositions : function(x, y, box)
31715     {
31716         var pos = [];
31717         
31718         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31719             
31720             pos.push({
31721                 x : x,
31722                 y : y
31723             });
31724
31725             pos.push({
31726                 x : x + (this.unitWidth + this.gutter) * 1,
31727                 y : y
31728             });
31729             
31730             pos.push({
31731                 x : x + (this.unitWidth + this.gutter) * 2,
31732                 y : y
31733             });
31734             
31735             return pos;
31736             
31737         }
31738         
31739         if(box[0].size == 'xs' && box[1].size == 'xs'){
31740             
31741             pos.push({
31742                 x : x,
31743                 y : y
31744             });
31745
31746             pos.push({
31747                 x : x,
31748                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31749             });
31750             
31751             pos.push({
31752                 x : x + (this.unitWidth + this.gutter) * 1,
31753                 y : y
31754             });
31755             
31756             return pos;
31757             
31758         }
31759         
31760         pos.push({
31761             x : x,
31762             y : y
31763         });
31764
31765         pos.push({
31766             x : x + (this.unitWidth + this.gutter) * 2,
31767             y : y
31768         });
31769
31770         pos.push({
31771             x : x + (this.unitWidth + this.gutter) * 2,
31772             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31773         });
31774             
31775         return pos;
31776         
31777     },
31778     
31779     getVerticalFourBoxColPositions : function(x, y, box)
31780     {
31781         var pos = [];
31782         
31783         if(box[0].size == 'xs'){
31784             
31785             pos.push({
31786                 x : x,
31787                 y : y
31788             });
31789
31790             pos.push({
31791                 x : x,
31792                 y : y + (this.unitHeight + this.gutter) * 1
31793             });
31794             
31795             pos.push({
31796                 x : x,
31797                 y : y + (this.unitHeight + this.gutter) * 2
31798             });
31799             
31800             pos.push({
31801                 x : x + (this.unitWidth + this.gutter) * 1,
31802                 y : y
31803             });
31804             
31805             return pos;
31806             
31807         }
31808         
31809         pos.push({
31810             x : x,
31811             y : y
31812         });
31813
31814         pos.push({
31815             x : x + (this.unitWidth + this.gutter) * 2,
31816             y : y
31817         });
31818
31819         pos.push({
31820             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31821             y : y + (this.unitHeight + this.gutter) * 1
31822         });
31823
31824         pos.push({
31825             x : x + (this.unitWidth + this.gutter) * 2,
31826             y : y + (this.unitWidth + this.gutter) * 2
31827         });
31828
31829         return pos;
31830         
31831     },
31832     
31833     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31834     {
31835         var pos = [];
31836         
31837         if(box[0].size == 'md-left'){
31838             pos.push({
31839                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31840                 y : minY
31841             });
31842             
31843             return pos;
31844         }
31845         
31846         if(box[0].size == 'md-right'){
31847             pos.push({
31848                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31849                 y : minY + (this.unitWidth + this.gutter) * 1
31850             });
31851             
31852             return pos;
31853         }
31854         
31855         var rand = Math.floor(Math.random() * (4 - box[0].y));
31856         
31857         pos.push({
31858             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31859             y : minY + (this.unitWidth + this.gutter) * rand
31860         });
31861         
31862         return pos;
31863         
31864     },
31865     
31866     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31867     {
31868         var pos = [];
31869         
31870         if(box[0].size == 'xs'){
31871             
31872             pos.push({
31873                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31874                 y : minY
31875             });
31876
31877             pos.push({
31878                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31879                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31880             });
31881             
31882             return pos;
31883             
31884         }
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) * 2
31894         });
31895         
31896         return pos;
31897         
31898     },
31899     
31900     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31901     {
31902         var pos = [];
31903         
31904         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31905             
31906             pos.push({
31907                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31908                 y : minY
31909             });
31910
31911             pos.push({
31912                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31913                 y : minY + (this.unitWidth + this.gutter) * 1
31914             });
31915             
31916             pos.push({
31917                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31918                 y : minY + (this.unitWidth + this.gutter) * 2
31919             });
31920             
31921             return pos;
31922             
31923         }
31924         
31925         if(box[0].size == 'xs' && box[1].size == 'xs'){
31926             
31927             pos.push({
31928                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31929                 y : minY
31930             });
31931
31932             pos.push({
31933                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31934                 y : minY
31935             });
31936             
31937             pos.push({
31938                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31939                 y : minY + (this.unitWidth + this.gutter) * 1
31940             });
31941             
31942             return pos;
31943             
31944         }
31945         
31946         pos.push({
31947             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31948             y : minY
31949         });
31950
31951         pos.push({
31952             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31953             y : minY + (this.unitWidth + this.gutter) * 2
31954         });
31955
31956         pos.push({
31957             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31958             y : minY + (this.unitWidth + this.gutter) * 2
31959         });
31960             
31961         return pos;
31962         
31963     },
31964     
31965     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31966     {
31967         var pos = [];
31968         
31969         if(box[0].size == 'xs'){
31970             
31971             pos.push({
31972                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31973                 y : minY
31974             });
31975
31976             pos.push({
31977                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31978                 y : minY
31979             });
31980             
31981             pos.push({
31982                 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),
31983                 y : minY
31984             });
31985             
31986             pos.push({
31987                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31988                 y : minY + (this.unitWidth + this.gutter) * 1
31989             });
31990             
31991             return pos;
31992             
31993         }
31994         
31995         pos.push({
31996             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31997             y : minY
31998         });
31999         
32000         pos.push({
32001             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002             y : minY + (this.unitWidth + this.gutter) * 2
32003         });
32004         
32005         pos.push({
32006             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32007             y : minY + (this.unitWidth + this.gutter) * 2
32008         });
32009         
32010         pos.push({
32011             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),
32012             y : minY + (this.unitWidth + this.gutter) * 2
32013         });
32014
32015         return pos;
32016         
32017     },
32018     
32019     /**
32020     * remove a Masonry Brick
32021     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32022     */
32023     removeBrick : function(brick_id)
32024     {
32025         if (!brick_id) {
32026             return;
32027         }
32028         
32029         for (var i = 0; i<this.bricks.length; i++) {
32030             if (this.bricks[i].id == brick_id) {
32031                 this.bricks.splice(i,1);
32032                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32033                 this.initial();
32034             }
32035         }
32036     },
32037     
32038     /**
32039     * adds a Masonry Brick
32040     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32041     */
32042     addBrick : function(cfg)
32043     {
32044         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32045         //this.register(cn);
32046         cn.parentId = this.id;
32047         cn.render(this.el);
32048         return cn;
32049     },
32050     
32051     /**
32052     * register a Masonry Brick
32053     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32054     */
32055     
32056     register : function(brick)
32057     {
32058         this.bricks.push(brick);
32059         brick.masonryId = this.id;
32060     },
32061     
32062     /**
32063     * clear all the Masonry Brick
32064     */
32065     clearAll : function()
32066     {
32067         this.bricks = [];
32068         //this.getChildContainer().dom.innerHTML = "";
32069         this.el.dom.innerHTML = '';
32070     },
32071     
32072     getSelected : function()
32073     {
32074         if (!this.selectedBrick) {
32075             return false;
32076         }
32077         
32078         return this.selectedBrick;
32079     }
32080 });
32081
32082 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32083     
32084     groups: {},
32085      /**
32086     * register a Masonry Layout
32087     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32088     */
32089     
32090     register : function(layout)
32091     {
32092         this.groups[layout.id] = layout;
32093     },
32094     /**
32095     * fetch a  Masonry Layout based on the masonry layout ID
32096     * @param {string} the masonry layout to add
32097     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32098     */
32099     
32100     get: function(layout_id) {
32101         if (typeof(this.groups[layout_id]) == 'undefined') {
32102             return false;
32103         }
32104         return this.groups[layout_id] ;
32105     }
32106     
32107     
32108     
32109 });
32110
32111  
32112
32113  /**
32114  *
32115  * This is based on 
32116  * http://masonry.desandro.com
32117  *
32118  * The idea is to render all the bricks based on vertical width...
32119  *
32120  * The original code extends 'outlayer' - we might need to use that....
32121  * 
32122  */
32123
32124
32125 /**
32126  * @class Roo.bootstrap.LayoutMasonryAuto
32127  * @extends Roo.bootstrap.Component
32128  * Bootstrap Layout Masonry class
32129  * 
32130  * @constructor
32131  * Create a new Element
32132  * @param {Object} config The config object
32133  */
32134
32135 Roo.bootstrap.LayoutMasonryAuto = function(config){
32136     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32137 };
32138
32139 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32140     
32141       /**
32142      * @cfg {Boolean} isFitWidth  - resize the width..
32143      */   
32144     isFitWidth : false,  // options..
32145     /**
32146      * @cfg {Boolean} isOriginLeft = left align?
32147      */   
32148     isOriginLeft : true,
32149     /**
32150      * @cfg {Boolean} isOriginTop = top align?
32151      */   
32152     isOriginTop : false,
32153     /**
32154      * @cfg {Boolean} isLayoutInstant = no animation?
32155      */   
32156     isLayoutInstant : false, // needed?
32157     /**
32158      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32159      */   
32160     isResizingContainer : true,
32161     /**
32162      * @cfg {Number} columnWidth  width of the columns 
32163      */   
32164     
32165     columnWidth : 0,
32166     
32167     /**
32168      * @cfg {Number} maxCols maximum number of columns
32169      */   
32170     
32171     maxCols: 0,
32172     /**
32173      * @cfg {Number} padHeight padding below box..
32174      */   
32175     
32176     padHeight : 10, 
32177     
32178     /**
32179      * @cfg {Boolean} isAutoInitial defalut true
32180      */   
32181     
32182     isAutoInitial : true, 
32183     
32184     // private?
32185     gutter : 0,
32186     
32187     containerWidth: 0,
32188     initialColumnWidth : 0,
32189     currentSize : null,
32190     
32191     colYs : null, // array.
32192     maxY : 0,
32193     padWidth: 10,
32194     
32195     
32196     tag: 'div',
32197     cls: '',
32198     bricks: null, //CompositeElement
32199     cols : 0, // array?
32200     // element : null, // wrapped now this.el
32201     _isLayoutInited : null, 
32202     
32203     
32204     getAutoCreate : function(){
32205         
32206         var cfg = {
32207             tag: this.tag,
32208             cls: 'blog-masonary-wrapper ' + this.cls,
32209             cn : {
32210                 cls : 'mas-boxes masonary'
32211             }
32212         };
32213         
32214         return cfg;
32215     },
32216     
32217     getChildContainer: function( )
32218     {
32219         if (this.boxesEl) {
32220             return this.boxesEl;
32221         }
32222         
32223         this.boxesEl = this.el.select('.mas-boxes').first();
32224         
32225         return this.boxesEl;
32226     },
32227     
32228     
32229     initEvents : function()
32230     {
32231         var _this = this;
32232         
32233         if(this.isAutoInitial){
32234             Roo.log('hook children rendered');
32235             this.on('childrenrendered', function() {
32236                 Roo.log('children rendered');
32237                 _this.initial();
32238             } ,this);
32239         }
32240         
32241     },
32242     
32243     initial : function()
32244     {
32245         this.reloadItems();
32246
32247         this.currentSize = this.el.getBox(true);
32248
32249         /// was window resize... - let's see if this works..
32250         Roo.EventManager.onWindowResize(this.resize, this); 
32251
32252         if(!this.isAutoInitial){
32253             this.layout();
32254             return;
32255         }
32256         
32257         this.layout.defer(500,this);
32258     },
32259     
32260     reloadItems: function()
32261     {
32262         this.bricks = this.el.select('.masonry-brick', true);
32263         
32264         this.bricks.each(function(b) {
32265             //Roo.log(b.getSize());
32266             if (!b.attr('originalwidth')) {
32267                 b.attr('originalwidth',  b.getSize().width);
32268             }
32269             
32270         });
32271         
32272         Roo.log(this.bricks.elements.length);
32273     },
32274     
32275     resize : function()
32276     {
32277         Roo.log('resize');
32278         var cs = this.el.getBox(true);
32279         
32280         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32281             Roo.log("no change in with or X");
32282             return;
32283         }
32284         this.currentSize = cs;
32285         this.layout();
32286     },
32287     
32288     layout : function()
32289     {
32290          Roo.log('layout');
32291         this._resetLayout();
32292         //this._manageStamps();
32293       
32294         // don't animate first layout
32295         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32296         this.layoutItems( isInstant );
32297       
32298         // flag for initalized
32299         this._isLayoutInited = true;
32300     },
32301     
32302     layoutItems : function( isInstant )
32303     {
32304         //var items = this._getItemsForLayout( this.items );
32305         // original code supports filtering layout items.. we just ignore it..
32306         
32307         this._layoutItems( this.bricks , isInstant );
32308       
32309         this._postLayout();
32310     },
32311     _layoutItems : function ( items , isInstant)
32312     {
32313        //this.fireEvent( 'layout', this, items );
32314     
32315
32316         if ( !items || !items.elements.length ) {
32317           // no items, emit event with empty array
32318             return;
32319         }
32320
32321         var queue = [];
32322         items.each(function(item) {
32323             Roo.log("layout item");
32324             Roo.log(item);
32325             // get x/y object from method
32326             var position = this._getItemLayoutPosition( item );
32327             // enqueue
32328             position.item = item;
32329             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32330             queue.push( position );
32331         }, this);
32332       
32333         this._processLayoutQueue( queue );
32334     },
32335     /** Sets position of item in DOM
32336     * @param {Element} item
32337     * @param {Number} x - horizontal position
32338     * @param {Number} y - vertical position
32339     * @param {Boolean} isInstant - disables transitions
32340     */
32341     _processLayoutQueue : function( queue )
32342     {
32343         for ( var i=0, len = queue.length; i < len; i++ ) {
32344             var obj = queue[i];
32345             obj.item.position('absolute');
32346             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32347         }
32348     },
32349       
32350     
32351     /**
32352     * Any logic you want to do after each layout,
32353     * i.e. size the container
32354     */
32355     _postLayout : function()
32356     {
32357         this.resizeContainer();
32358     },
32359     
32360     resizeContainer : function()
32361     {
32362         if ( !this.isResizingContainer ) {
32363             return;
32364         }
32365         var size = this._getContainerSize();
32366         if ( size ) {
32367             this.el.setSize(size.width,size.height);
32368             this.boxesEl.setSize(size.width,size.height);
32369         }
32370     },
32371     
32372     
32373     
32374     _resetLayout : function()
32375     {
32376         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32377         this.colWidth = this.el.getWidth();
32378         //this.gutter = this.el.getWidth(); 
32379         
32380         this.measureColumns();
32381
32382         // reset column Y
32383         var i = this.cols;
32384         this.colYs = [];
32385         while (i--) {
32386             this.colYs.push( 0 );
32387         }
32388     
32389         this.maxY = 0;
32390     },
32391
32392     measureColumns : function()
32393     {
32394         this.getContainerWidth();
32395       // if columnWidth is 0, default to outerWidth of first item
32396         if ( !this.columnWidth ) {
32397             var firstItem = this.bricks.first();
32398             Roo.log(firstItem);
32399             this.columnWidth  = this.containerWidth;
32400             if (firstItem && firstItem.attr('originalwidth') ) {
32401                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32402             }
32403             // columnWidth fall back to item of first element
32404             Roo.log("set column width?");
32405                         this.initialColumnWidth = this.columnWidth  ;
32406
32407             // if first elem has no width, default to size of container
32408             
32409         }
32410         
32411         
32412         if (this.initialColumnWidth) {
32413             this.columnWidth = this.initialColumnWidth;
32414         }
32415         
32416         
32417             
32418         // column width is fixed at the top - however if container width get's smaller we should
32419         // reduce it...
32420         
32421         // this bit calcs how man columns..
32422             
32423         var columnWidth = this.columnWidth += this.gutter;
32424       
32425         // calculate columns
32426         var containerWidth = this.containerWidth + this.gutter;
32427         
32428         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32429         // fix rounding errors, typically with gutters
32430         var excess = columnWidth - containerWidth % columnWidth;
32431         
32432         
32433         // if overshoot is less than a pixel, round up, otherwise floor it
32434         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32435         cols = Math[ mathMethod ]( cols );
32436         this.cols = Math.max( cols, 1 );
32437         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32438         
32439          // padding positioning..
32440         var totalColWidth = this.cols * this.columnWidth;
32441         var padavail = this.containerWidth - totalColWidth;
32442         // so for 2 columns - we need 3 'pads'
32443         
32444         var padNeeded = (1+this.cols) * this.padWidth;
32445         
32446         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32447         
32448         this.columnWidth += padExtra
32449         //this.padWidth = Math.floor(padavail /  ( this.cols));
32450         
32451         // adjust colum width so that padding is fixed??
32452         
32453         // we have 3 columns ... total = width * 3
32454         // we have X left over... that should be used by 
32455         
32456         //if (this.expandC) {
32457             
32458         //}
32459         
32460         
32461         
32462     },
32463     
32464     getContainerWidth : function()
32465     {
32466        /* // container is parent if fit width
32467         var container = this.isFitWidth ? this.element.parentNode : this.element;
32468         // check that this.size and size are there
32469         // IE8 triggers resize on body size change, so they might not be
32470         
32471         var size = getSize( container );  //FIXME
32472         this.containerWidth = size && size.innerWidth; //FIXME
32473         */
32474          
32475         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32476         
32477     },
32478     
32479     _getItemLayoutPosition : function( item )  // what is item?
32480     {
32481         // we resize the item to our columnWidth..
32482       
32483         item.setWidth(this.columnWidth);
32484         item.autoBoxAdjust  = false;
32485         
32486         var sz = item.getSize();
32487  
32488         // how many columns does this brick span
32489         var remainder = this.containerWidth % this.columnWidth;
32490         
32491         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32492         // round if off by 1 pixel, otherwise use ceil
32493         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32494         colSpan = Math.min( colSpan, this.cols );
32495         
32496         // normally this should be '1' as we dont' currently allow multi width columns..
32497         
32498         var colGroup = this._getColGroup( colSpan );
32499         // get the minimum Y value from the columns
32500         var minimumY = Math.min.apply( Math, colGroup );
32501         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32502         
32503         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32504          
32505         // position the brick
32506         var position = {
32507             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32508             y: this.currentSize.y + minimumY + this.padHeight
32509         };
32510         
32511         Roo.log(position);
32512         // apply setHeight to necessary columns
32513         var setHeight = minimumY + sz.height + this.padHeight;
32514         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32515         
32516         var setSpan = this.cols + 1 - colGroup.length;
32517         for ( var i = 0; i < setSpan; i++ ) {
32518           this.colYs[ shortColIndex + i ] = setHeight ;
32519         }
32520       
32521         return position;
32522     },
32523     
32524     /**
32525      * @param {Number} colSpan - number of columns the element spans
32526      * @returns {Array} colGroup
32527      */
32528     _getColGroup : function( colSpan )
32529     {
32530         if ( colSpan < 2 ) {
32531           // if brick spans only one column, use all the column Ys
32532           return this.colYs;
32533         }
32534       
32535         var colGroup = [];
32536         // how many different places could this brick fit horizontally
32537         var groupCount = this.cols + 1 - colSpan;
32538         // for each group potential horizontal position
32539         for ( var i = 0; i < groupCount; i++ ) {
32540           // make an array of colY values for that one group
32541           var groupColYs = this.colYs.slice( i, i + colSpan );
32542           // and get the max value of the array
32543           colGroup[i] = Math.max.apply( Math, groupColYs );
32544         }
32545         return colGroup;
32546     },
32547     /*
32548     _manageStamp : function( stamp )
32549     {
32550         var stampSize =  stamp.getSize();
32551         var offset = stamp.getBox();
32552         // get the columns that this stamp affects
32553         var firstX = this.isOriginLeft ? offset.x : offset.right;
32554         var lastX = firstX + stampSize.width;
32555         var firstCol = Math.floor( firstX / this.columnWidth );
32556         firstCol = Math.max( 0, firstCol );
32557         
32558         var lastCol = Math.floor( lastX / this.columnWidth );
32559         // lastCol should not go over if multiple of columnWidth #425
32560         lastCol -= lastX % this.columnWidth ? 0 : 1;
32561         lastCol = Math.min( this.cols - 1, lastCol );
32562         
32563         // set colYs to bottom of the stamp
32564         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32565             stampSize.height;
32566             
32567         for ( var i = firstCol; i <= lastCol; i++ ) {
32568           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32569         }
32570     },
32571     */
32572     
32573     _getContainerSize : function()
32574     {
32575         this.maxY = Math.max.apply( Math, this.colYs );
32576         var size = {
32577             height: this.maxY
32578         };
32579       
32580         if ( this.isFitWidth ) {
32581             size.width = this._getContainerFitWidth();
32582         }
32583       
32584         return size;
32585     },
32586     
32587     _getContainerFitWidth : function()
32588     {
32589         var unusedCols = 0;
32590         // count unused columns
32591         var i = this.cols;
32592         while ( --i ) {
32593           if ( this.colYs[i] !== 0 ) {
32594             break;
32595           }
32596           unusedCols++;
32597         }
32598         // fit container to columns that have been used
32599         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32600     },
32601     
32602     needsResizeLayout : function()
32603     {
32604         var previousWidth = this.containerWidth;
32605         this.getContainerWidth();
32606         return previousWidth !== this.containerWidth;
32607     }
32608  
32609 });
32610
32611  
32612
32613  /*
32614  * - LGPL
32615  *
32616  * element
32617  * 
32618  */
32619
32620 /**
32621  * @class Roo.bootstrap.MasonryBrick
32622  * @extends Roo.bootstrap.Component
32623  * Bootstrap MasonryBrick class
32624  * 
32625  * @constructor
32626  * Create a new MasonryBrick
32627  * @param {Object} config The config object
32628  */
32629
32630 Roo.bootstrap.MasonryBrick = function(config){
32631     
32632     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32633     
32634     Roo.bootstrap.MasonryBrick.register(this);
32635     
32636     this.addEvents({
32637         // raw events
32638         /**
32639          * @event click
32640          * When a MasonryBrick is clcik
32641          * @param {Roo.bootstrap.MasonryBrick} this
32642          * @param {Roo.EventObject} e
32643          */
32644         "click" : true
32645     });
32646 };
32647
32648 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32649     
32650     /**
32651      * @cfg {String} title
32652      */   
32653     title : '',
32654     /**
32655      * @cfg {String} html
32656      */   
32657     html : '',
32658     /**
32659      * @cfg {String} bgimage
32660      */   
32661     bgimage : '',
32662     /**
32663      * @cfg {String} videourl
32664      */   
32665     videourl : '',
32666     /**
32667      * @cfg {String} cls
32668      */   
32669     cls : '',
32670     /**
32671      * @cfg {String} href
32672      */   
32673     href : '',
32674     /**
32675      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32676      */   
32677     size : 'xs',
32678     
32679     /**
32680      * @cfg {String} placetitle (center|bottom)
32681      */   
32682     placetitle : '',
32683     
32684     /**
32685      * @cfg {Boolean} isFitContainer defalut true
32686      */   
32687     isFitContainer : true, 
32688     
32689     /**
32690      * @cfg {Boolean} preventDefault defalut false
32691      */   
32692     preventDefault : false, 
32693     
32694     /**
32695      * @cfg {Boolean} inverse defalut false
32696      */   
32697     maskInverse : false, 
32698     
32699     getAutoCreate : function()
32700     {
32701         if(!this.isFitContainer){
32702             return this.getSplitAutoCreate();
32703         }
32704         
32705         var cls = 'masonry-brick masonry-brick-full';
32706         
32707         if(this.href.length){
32708             cls += ' masonry-brick-link';
32709         }
32710         
32711         if(this.bgimage.length){
32712             cls += ' masonry-brick-image';
32713         }
32714         
32715         if(this.maskInverse){
32716             cls += ' mask-inverse';
32717         }
32718         
32719         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32720             cls += ' enable-mask';
32721         }
32722         
32723         if(this.size){
32724             cls += ' masonry-' + this.size + '-brick';
32725         }
32726         
32727         if(this.placetitle.length){
32728             
32729             switch (this.placetitle) {
32730                 case 'center' :
32731                     cls += ' masonry-center-title';
32732                     break;
32733                 case 'bottom' :
32734                     cls += ' masonry-bottom-title';
32735                     break;
32736                 default:
32737                     break;
32738             }
32739             
32740         } else {
32741             if(!this.html.length && !this.bgimage.length){
32742                 cls += ' masonry-center-title';
32743             }
32744
32745             if(!this.html.length && this.bgimage.length){
32746                 cls += ' masonry-bottom-title';
32747             }
32748         }
32749         
32750         if(this.cls){
32751             cls += ' ' + this.cls;
32752         }
32753         
32754         var cfg = {
32755             tag: (this.href.length) ? 'a' : 'div',
32756             cls: cls,
32757             cn: [
32758                 {
32759                     tag: 'div',
32760                     cls: 'masonry-brick-mask'
32761                 },
32762                 {
32763                     tag: 'div',
32764                     cls: 'masonry-brick-paragraph',
32765                     cn: []
32766                 }
32767             ]
32768         };
32769         
32770         if(this.href.length){
32771             cfg.href = this.href;
32772         }
32773         
32774         var cn = cfg.cn[1].cn;
32775         
32776         if(this.title.length){
32777             cn.push({
32778                 tag: 'h4',
32779                 cls: 'masonry-brick-title',
32780                 html: this.title
32781             });
32782         }
32783         
32784         if(this.html.length){
32785             cn.push({
32786                 tag: 'p',
32787                 cls: 'masonry-brick-text',
32788                 html: this.html
32789             });
32790         }
32791         
32792         if (!this.title.length && !this.html.length) {
32793             cfg.cn[1].cls += ' hide';
32794         }
32795         
32796         if(this.bgimage.length){
32797             cfg.cn.push({
32798                 tag: 'img',
32799                 cls: 'masonry-brick-image-view',
32800                 src: this.bgimage
32801             });
32802         }
32803         
32804         if(this.videourl.length){
32805             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32806             // youtube support only?
32807             cfg.cn.push({
32808                 tag: 'iframe',
32809                 cls: 'masonry-brick-image-view',
32810                 src: vurl,
32811                 frameborder : 0,
32812                 allowfullscreen : true
32813             });
32814         }
32815         
32816         return cfg;
32817         
32818     },
32819     
32820     getSplitAutoCreate : function()
32821     {
32822         var cls = 'masonry-brick masonry-brick-split';
32823         
32824         if(this.href.length){
32825             cls += ' masonry-brick-link';
32826         }
32827         
32828         if(this.bgimage.length){
32829             cls += ' masonry-brick-image';
32830         }
32831         
32832         if(this.size){
32833             cls += ' masonry-' + this.size + '-brick';
32834         }
32835         
32836         switch (this.placetitle) {
32837             case 'center' :
32838                 cls += ' masonry-center-title';
32839                 break;
32840             case 'bottom' :
32841                 cls += ' masonry-bottom-title';
32842                 break;
32843             default:
32844                 if(!this.bgimage.length){
32845                     cls += ' masonry-center-title';
32846                 }
32847
32848                 if(this.bgimage.length){
32849                     cls += ' masonry-bottom-title';
32850                 }
32851                 break;
32852         }
32853         
32854         if(this.cls){
32855             cls += ' ' + this.cls;
32856         }
32857         
32858         var cfg = {
32859             tag: (this.href.length) ? 'a' : 'div',
32860             cls: cls,
32861             cn: [
32862                 {
32863                     tag: 'div',
32864                     cls: 'masonry-brick-split-head',
32865                     cn: [
32866                         {
32867                             tag: 'div',
32868                             cls: 'masonry-brick-paragraph',
32869                             cn: []
32870                         }
32871                     ]
32872                 },
32873                 {
32874                     tag: 'div',
32875                     cls: 'masonry-brick-split-body',
32876                     cn: []
32877                 }
32878             ]
32879         };
32880         
32881         if(this.href.length){
32882             cfg.href = this.href;
32883         }
32884         
32885         if(this.title.length){
32886             cfg.cn[0].cn[0].cn.push({
32887                 tag: 'h4',
32888                 cls: 'masonry-brick-title',
32889                 html: this.title
32890             });
32891         }
32892         
32893         if(this.html.length){
32894             cfg.cn[1].cn.push({
32895                 tag: 'p',
32896                 cls: 'masonry-brick-text',
32897                 html: this.html
32898             });
32899         }
32900
32901         if(this.bgimage.length){
32902             cfg.cn[0].cn.push({
32903                 tag: 'img',
32904                 cls: 'masonry-brick-image-view',
32905                 src: this.bgimage
32906             });
32907         }
32908         
32909         if(this.videourl.length){
32910             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32911             // youtube support only?
32912             cfg.cn[0].cn.cn.push({
32913                 tag: 'iframe',
32914                 cls: 'masonry-brick-image-view',
32915                 src: vurl,
32916                 frameborder : 0,
32917                 allowfullscreen : true
32918             });
32919         }
32920         
32921         return cfg;
32922     },
32923     
32924     initEvents: function() 
32925     {
32926         switch (this.size) {
32927             case 'xs' :
32928                 this.x = 1;
32929                 this.y = 1;
32930                 break;
32931             case 'sm' :
32932                 this.x = 2;
32933                 this.y = 2;
32934                 break;
32935             case 'md' :
32936             case 'md-left' :
32937             case 'md-right' :
32938                 this.x = 3;
32939                 this.y = 3;
32940                 break;
32941             case 'tall' :
32942                 this.x = 2;
32943                 this.y = 3;
32944                 break;
32945             case 'wide' :
32946                 this.x = 3;
32947                 this.y = 2;
32948                 break;
32949             case 'wide-thin' :
32950                 this.x = 3;
32951                 this.y = 1;
32952                 break;
32953                         
32954             default :
32955                 break;
32956         }
32957         
32958         if(Roo.isTouch){
32959             this.el.on('touchstart', this.onTouchStart, this);
32960             this.el.on('touchmove', this.onTouchMove, this);
32961             this.el.on('touchend', this.onTouchEnd, this);
32962             this.el.on('contextmenu', this.onContextMenu, this);
32963         } else {
32964             this.el.on('mouseenter'  ,this.enter, this);
32965             this.el.on('mouseleave', this.leave, this);
32966             this.el.on('click', this.onClick, this);
32967         }
32968         
32969         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32970             this.parent().bricks.push(this);   
32971         }
32972         
32973     },
32974     
32975     onClick: function(e, el)
32976     {
32977         var time = this.endTimer - this.startTimer;
32978         // Roo.log(e.preventDefault());
32979         if(Roo.isTouch){
32980             if(time > 1000){
32981                 e.preventDefault();
32982                 return;
32983             }
32984         }
32985         
32986         if(!this.preventDefault){
32987             return;
32988         }
32989         
32990         e.preventDefault();
32991         
32992         if (this.activeClass != '') {
32993             this.selectBrick();
32994         }
32995         
32996         this.fireEvent('click', this, e);
32997     },
32998     
32999     enter: function(e, el)
33000     {
33001         e.preventDefault();
33002         
33003         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33004             return;
33005         }
33006         
33007         if(this.bgimage.length && this.html.length){
33008             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33009         }
33010     },
33011     
33012     leave: function(e, el)
33013     {
33014         e.preventDefault();
33015         
33016         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33017             return;
33018         }
33019         
33020         if(this.bgimage.length && this.html.length){
33021             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33022         }
33023     },
33024     
33025     onTouchStart: function(e, el)
33026     {
33027 //        e.preventDefault();
33028         
33029         this.touchmoved = false;
33030         
33031         if(!this.isFitContainer){
33032             return;
33033         }
33034         
33035         if(!this.bgimage.length || !this.html.length){
33036             return;
33037         }
33038         
33039         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33040         
33041         this.timer = new Date().getTime();
33042         
33043     },
33044     
33045     onTouchMove: function(e, el)
33046     {
33047         this.touchmoved = true;
33048     },
33049     
33050     onContextMenu : function(e,el)
33051     {
33052         e.preventDefault();
33053         e.stopPropagation();
33054         return false;
33055     },
33056     
33057     onTouchEnd: function(e, el)
33058     {
33059 //        e.preventDefault();
33060         
33061         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33062         
33063             this.leave(e,el);
33064             
33065             return;
33066         }
33067         
33068         if(!this.bgimage.length || !this.html.length){
33069             
33070             if(this.href.length){
33071                 window.location.href = this.href;
33072             }
33073             
33074             return;
33075         }
33076         
33077         if(!this.isFitContainer){
33078             return;
33079         }
33080         
33081         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33082         
33083         window.location.href = this.href;
33084     },
33085     
33086     //selection on single brick only
33087     selectBrick : function() {
33088         
33089         if (!this.parentId) {
33090             return;
33091         }
33092         
33093         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33094         var index = m.selectedBrick.indexOf(this.id);
33095         
33096         if ( index > -1) {
33097             m.selectedBrick.splice(index,1);
33098             this.el.removeClass(this.activeClass);
33099             return;
33100         }
33101         
33102         for(var i = 0; i < m.selectedBrick.length; i++) {
33103             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33104             b.el.removeClass(b.activeClass);
33105         }
33106         
33107         m.selectedBrick = [];
33108         
33109         m.selectedBrick.push(this.id);
33110         this.el.addClass(this.activeClass);
33111         return;
33112     },
33113     
33114     isSelected : function(){
33115         return this.el.hasClass(this.activeClass);
33116         
33117     }
33118 });
33119
33120 Roo.apply(Roo.bootstrap.MasonryBrick, {
33121     
33122     //groups: {},
33123     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33124      /**
33125     * register a Masonry Brick
33126     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33127     */
33128     
33129     register : function(brick)
33130     {
33131         //this.groups[brick.id] = brick;
33132         this.groups.add(brick.id, brick);
33133     },
33134     /**
33135     * fetch a  masonry brick based on the masonry brick ID
33136     * @param {string} the masonry brick to add
33137     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33138     */
33139     
33140     get: function(brick_id) 
33141     {
33142         // if (typeof(this.groups[brick_id]) == 'undefined') {
33143         //     return false;
33144         // }
33145         // return this.groups[brick_id] ;
33146         
33147         if(this.groups.key(brick_id)) {
33148             return this.groups.key(brick_id);
33149         }
33150         
33151         return false;
33152     }
33153     
33154     
33155     
33156 });
33157
33158  /*
33159  * - LGPL
33160  *
33161  * element
33162  * 
33163  */
33164
33165 /**
33166  * @class Roo.bootstrap.Brick
33167  * @extends Roo.bootstrap.Component
33168  * Bootstrap Brick class
33169  * 
33170  * @constructor
33171  * Create a new Brick
33172  * @param {Object} config The config object
33173  */
33174
33175 Roo.bootstrap.Brick = function(config){
33176     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33177     
33178     this.addEvents({
33179         // raw events
33180         /**
33181          * @event click
33182          * When a Brick is click
33183          * @param {Roo.bootstrap.Brick} this
33184          * @param {Roo.EventObject} e
33185          */
33186         "click" : true
33187     });
33188 };
33189
33190 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33191     
33192     /**
33193      * @cfg {String} title
33194      */   
33195     title : '',
33196     /**
33197      * @cfg {String} html
33198      */   
33199     html : '',
33200     /**
33201      * @cfg {String} bgimage
33202      */   
33203     bgimage : '',
33204     /**
33205      * @cfg {String} cls
33206      */   
33207     cls : '',
33208     /**
33209      * @cfg {String} href
33210      */   
33211     href : '',
33212     /**
33213      * @cfg {String} video
33214      */   
33215     video : '',
33216     /**
33217      * @cfg {Boolean} square
33218      */   
33219     square : true,
33220     
33221     getAutoCreate : function()
33222     {
33223         var cls = 'roo-brick';
33224         
33225         if(this.href.length){
33226             cls += ' roo-brick-link';
33227         }
33228         
33229         if(this.bgimage.length){
33230             cls += ' roo-brick-image';
33231         }
33232         
33233         if(!this.html.length && !this.bgimage.length){
33234             cls += ' roo-brick-center-title';
33235         }
33236         
33237         if(!this.html.length && this.bgimage.length){
33238             cls += ' roo-brick-bottom-title';
33239         }
33240         
33241         if(this.cls){
33242             cls += ' ' + this.cls;
33243         }
33244         
33245         var cfg = {
33246             tag: (this.href.length) ? 'a' : 'div',
33247             cls: cls,
33248             cn: [
33249                 {
33250                     tag: 'div',
33251                     cls: 'roo-brick-paragraph',
33252                     cn: []
33253                 }
33254             ]
33255         };
33256         
33257         if(this.href.length){
33258             cfg.href = this.href;
33259         }
33260         
33261         var cn = cfg.cn[0].cn;
33262         
33263         if(this.title.length){
33264             cn.push({
33265                 tag: 'h4',
33266                 cls: 'roo-brick-title',
33267                 html: this.title
33268             });
33269         }
33270         
33271         if(this.html.length){
33272             cn.push({
33273                 tag: 'p',
33274                 cls: 'roo-brick-text',
33275                 html: this.html
33276             });
33277         } else {
33278             cn.cls += ' hide';
33279         }
33280         
33281         if(this.bgimage.length){
33282             cfg.cn.push({
33283                 tag: 'img',
33284                 cls: 'roo-brick-image-view',
33285                 src: this.bgimage
33286             });
33287         }
33288         
33289         return cfg;
33290     },
33291     
33292     initEvents: function() 
33293     {
33294         if(this.title.length || this.html.length){
33295             this.el.on('mouseenter'  ,this.enter, this);
33296             this.el.on('mouseleave', this.leave, this);
33297         }
33298         
33299         Roo.EventManager.onWindowResize(this.resize, this); 
33300         
33301         if(this.bgimage.length){
33302             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33303             this.imageEl.on('load', this.onImageLoad, this);
33304             return;
33305         }
33306         
33307         this.resize();
33308     },
33309     
33310     onImageLoad : function()
33311     {
33312         this.resize();
33313     },
33314     
33315     resize : function()
33316     {
33317         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33318         
33319         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33320         
33321         if(this.bgimage.length){
33322             var image = this.el.select('.roo-brick-image-view', true).first();
33323             
33324             image.setWidth(paragraph.getWidth());
33325             
33326             if(this.square){
33327                 image.setHeight(paragraph.getWidth());
33328             }
33329             
33330             this.el.setHeight(image.getHeight());
33331             paragraph.setHeight(image.getHeight());
33332             
33333         }
33334         
33335     },
33336     
33337     enter: function(e, el)
33338     {
33339         e.preventDefault();
33340         
33341         if(this.bgimage.length){
33342             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33343             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33344         }
33345     },
33346     
33347     leave: function(e, el)
33348     {
33349         e.preventDefault();
33350         
33351         if(this.bgimage.length){
33352             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33353             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33354         }
33355     }
33356     
33357 });
33358
33359  
33360
33361  /*
33362  * - LGPL
33363  *
33364  * Number field 
33365  */
33366
33367 /**
33368  * @class Roo.bootstrap.NumberField
33369  * @extends Roo.bootstrap.Input
33370  * Bootstrap NumberField class
33371  * 
33372  * 
33373  * 
33374  * 
33375  * @constructor
33376  * Create a new NumberField
33377  * @param {Object} config The config object
33378  */
33379
33380 Roo.bootstrap.NumberField = function(config){
33381     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33382 };
33383
33384 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33385     
33386     /**
33387      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33388      */
33389     allowDecimals : true,
33390     /**
33391      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33392      */
33393     decimalSeparator : ".",
33394     /**
33395      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33396      */
33397     decimalPrecision : 2,
33398     /**
33399      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33400      */
33401     allowNegative : true,
33402     
33403     /**
33404      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33405      */
33406     allowZero: true,
33407     /**
33408      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33409      */
33410     minValue : Number.NEGATIVE_INFINITY,
33411     /**
33412      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33413      */
33414     maxValue : Number.MAX_VALUE,
33415     /**
33416      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33417      */
33418     minText : "The minimum value for this field is {0}",
33419     /**
33420      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33421      */
33422     maxText : "The maximum value for this field is {0}",
33423     /**
33424      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33425      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33426      */
33427     nanText : "{0} is not a valid number",
33428     /**
33429      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33430      */
33431     thousandsDelimiter : false,
33432     /**
33433      * @cfg {String} valueAlign alignment of value
33434      */
33435     valueAlign : "left",
33436
33437     getAutoCreate : function()
33438     {
33439         var hiddenInput = {
33440             tag: 'input',
33441             type: 'hidden',
33442             id: Roo.id(),
33443             cls: 'hidden-number-input'
33444         };
33445         
33446         if (this.name) {
33447             hiddenInput.name = this.name;
33448         }
33449         
33450         this.name = '';
33451         
33452         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33453         
33454         this.name = hiddenInput.name;
33455         
33456         if(cfg.cn.length > 0) {
33457             cfg.cn.push(hiddenInput);
33458         }
33459         
33460         return cfg;
33461     },
33462
33463     // private
33464     initEvents : function()
33465     {   
33466         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33467         
33468         var allowed = "0123456789";
33469         
33470         if(this.allowDecimals){
33471             allowed += this.decimalSeparator;
33472         }
33473         
33474         if(this.allowNegative){
33475             allowed += "-";
33476         }
33477         
33478         if(this.thousandsDelimiter) {
33479             allowed += ",";
33480         }
33481         
33482         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33483         
33484         var keyPress = function(e){
33485             
33486             var k = e.getKey();
33487             
33488             var c = e.getCharCode();
33489             
33490             if(
33491                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33492                     allowed.indexOf(String.fromCharCode(c)) === -1
33493             ){
33494                 e.stopEvent();
33495                 return;
33496             }
33497             
33498             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33499                 return;
33500             }
33501             
33502             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33503                 e.stopEvent();
33504             }
33505         };
33506         
33507         this.el.on("keypress", keyPress, this);
33508     },
33509     
33510     validateValue : function(value)
33511     {
33512         
33513         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33514             return false;
33515         }
33516         
33517         var num = this.parseValue(value);
33518         
33519         if(isNaN(num)){
33520             this.markInvalid(String.format(this.nanText, value));
33521             return false;
33522         }
33523         
33524         if(num < this.minValue){
33525             this.markInvalid(String.format(this.minText, this.minValue));
33526             return false;
33527         }
33528         
33529         if(num > this.maxValue){
33530             this.markInvalid(String.format(this.maxText, this.maxValue));
33531             return false;
33532         }
33533         
33534         return true;
33535     },
33536
33537     getValue : function()
33538     {
33539         var v = this.hiddenEl().getValue();
33540         
33541         return this.fixPrecision(this.parseValue(v));
33542     },
33543
33544     parseValue : function(value)
33545     {
33546         if(this.thousandsDelimiter) {
33547             value += "";
33548             r = new RegExp(",", "g");
33549             value = value.replace(r, "");
33550         }
33551         
33552         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33553         return isNaN(value) ? '' : value;
33554     },
33555
33556     fixPrecision : function(value)
33557     {
33558         if(this.thousandsDelimiter) {
33559             value += "";
33560             r = new RegExp(",", "g");
33561             value = value.replace(r, "");
33562         }
33563         
33564         var nan = isNaN(value);
33565         
33566         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33567             return nan ? '' : value;
33568         }
33569         return parseFloat(value).toFixed(this.decimalPrecision);
33570     },
33571
33572     setValue : function(v)
33573     {
33574         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33575         
33576         this.value = v;
33577         
33578         if(this.rendered){
33579             
33580             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33581             
33582             this.inputEl().dom.value = (v == '') ? '' :
33583                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33584             
33585             if(!this.allowZero && v === '0') {
33586                 this.hiddenEl().dom.value = '';
33587                 this.inputEl().dom.value = '';
33588             }
33589             
33590             this.validate();
33591         }
33592     },
33593
33594     decimalPrecisionFcn : function(v)
33595     {
33596         return Math.floor(v);
33597     },
33598
33599     beforeBlur : function()
33600     {
33601         var v = this.parseValue(this.getRawValue());
33602         
33603         if(v || v === 0 || v === ''){
33604             this.setValue(v);
33605         }
33606     },
33607     
33608     hiddenEl : function()
33609     {
33610         return this.el.select('input.hidden-number-input',true).first();
33611     }
33612     
33613 });
33614
33615  
33616
33617 /*
33618 * Licence: LGPL
33619 */
33620
33621 /**
33622  * @class Roo.bootstrap.DocumentSlider
33623  * @extends Roo.bootstrap.Component
33624  * Bootstrap DocumentSlider class
33625  * 
33626  * @constructor
33627  * Create a new DocumentViewer
33628  * @param {Object} config The config object
33629  */
33630
33631 Roo.bootstrap.DocumentSlider = function(config){
33632     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33633     
33634     this.files = [];
33635     
33636     this.addEvents({
33637         /**
33638          * @event initial
33639          * Fire after initEvent
33640          * @param {Roo.bootstrap.DocumentSlider} this
33641          */
33642         "initial" : true,
33643         /**
33644          * @event update
33645          * Fire after update
33646          * @param {Roo.bootstrap.DocumentSlider} this
33647          */
33648         "update" : true,
33649         /**
33650          * @event click
33651          * Fire after click
33652          * @param {Roo.bootstrap.DocumentSlider} this
33653          */
33654         "click" : true
33655     });
33656 };
33657
33658 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33659     
33660     files : false,
33661     
33662     indicator : 0,
33663     
33664     getAutoCreate : function()
33665     {
33666         var cfg = {
33667             tag : 'div',
33668             cls : 'roo-document-slider',
33669             cn : [
33670                 {
33671                     tag : 'div',
33672                     cls : 'roo-document-slider-header',
33673                     cn : [
33674                         {
33675                             tag : 'div',
33676                             cls : 'roo-document-slider-header-title'
33677                         }
33678                     ]
33679                 },
33680                 {
33681                     tag : 'div',
33682                     cls : 'roo-document-slider-body',
33683                     cn : [
33684                         {
33685                             tag : 'div',
33686                             cls : 'roo-document-slider-prev',
33687                             cn : [
33688                                 {
33689                                     tag : 'i',
33690                                     cls : 'fa fa-chevron-left'
33691                                 }
33692                             ]
33693                         },
33694                         {
33695                             tag : 'div',
33696                             cls : 'roo-document-slider-thumb',
33697                             cn : [
33698                                 {
33699                                     tag : 'img',
33700                                     cls : 'roo-document-slider-image'
33701                                 }
33702                             ]
33703                         },
33704                         {
33705                             tag : 'div',
33706                             cls : 'roo-document-slider-next',
33707                             cn : [
33708                                 {
33709                                     tag : 'i',
33710                                     cls : 'fa fa-chevron-right'
33711                                 }
33712                             ]
33713                         }
33714                     ]
33715                 }
33716             ]
33717         };
33718         
33719         return cfg;
33720     },
33721     
33722     initEvents : function()
33723     {
33724         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33725         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33726         
33727         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33728         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33729         
33730         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33731         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33732         
33733         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33734         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33735         
33736         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33737         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33738         
33739         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33740         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33741         
33742         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33743         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33744         
33745         this.thumbEl.on('click', this.onClick, this);
33746         
33747         this.prevIndicator.on('click', this.prev, this);
33748         
33749         this.nextIndicator.on('click', this.next, this);
33750         
33751     },
33752     
33753     initial : function()
33754     {
33755         if(this.files.length){
33756             this.indicator = 1;
33757             this.update()
33758         }
33759         
33760         this.fireEvent('initial', this);
33761     },
33762     
33763     update : function()
33764     {
33765         this.imageEl.attr('src', this.files[this.indicator - 1]);
33766         
33767         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33768         
33769         this.prevIndicator.show();
33770         
33771         if(this.indicator == 1){
33772             this.prevIndicator.hide();
33773         }
33774         
33775         this.nextIndicator.show();
33776         
33777         if(this.indicator == this.files.length){
33778             this.nextIndicator.hide();
33779         }
33780         
33781         this.thumbEl.scrollTo('top');
33782         
33783         this.fireEvent('update', this);
33784     },
33785     
33786     onClick : function(e)
33787     {
33788         e.preventDefault();
33789         
33790         this.fireEvent('click', this);
33791     },
33792     
33793     prev : function(e)
33794     {
33795         e.preventDefault();
33796         
33797         this.indicator = Math.max(1, this.indicator - 1);
33798         
33799         this.update();
33800     },
33801     
33802     next : function(e)
33803     {
33804         e.preventDefault();
33805         
33806         this.indicator = Math.min(this.files.length, this.indicator + 1);
33807         
33808         this.update();
33809     }
33810 });
33811 /*
33812  * - LGPL
33813  *
33814  * RadioSet
33815  *
33816  *
33817  */
33818
33819 /**
33820  * @class Roo.bootstrap.RadioSet
33821  * @extends Roo.bootstrap.Input
33822  * Bootstrap RadioSet class
33823  * @cfg {String} indicatorpos (left|right) default left
33824  * @cfg {Boolean} inline (true|false) inline the element (default true)
33825  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33826  * @constructor
33827  * Create a new RadioSet
33828  * @param {Object} config The config object
33829  */
33830
33831 Roo.bootstrap.RadioSet = function(config){
33832     
33833     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33834     
33835     this.radioes = [];
33836     
33837     Roo.bootstrap.RadioSet.register(this);
33838     
33839     this.addEvents({
33840         /**
33841         * @event check
33842         * Fires when the element is checked or unchecked.
33843         * @param {Roo.bootstrap.RadioSet} this This radio
33844         * @param {Roo.bootstrap.Radio} item The checked item
33845         */
33846        check : true,
33847        /**
33848         * @event click
33849         * Fires when the element is click.
33850         * @param {Roo.bootstrap.RadioSet} this This radio set
33851         * @param {Roo.bootstrap.Radio} item The checked item
33852         * @param {Roo.EventObject} e The event object
33853         */
33854        click : true
33855     });
33856     
33857 };
33858
33859 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33860
33861     radioes : false,
33862     
33863     inline : true,
33864     
33865     weight : '',
33866     
33867     indicatorpos : 'left',
33868     
33869     getAutoCreate : function()
33870     {
33871         var label = {
33872             tag : 'label',
33873             cls : 'roo-radio-set-label',
33874             cn : [
33875                 {
33876                     tag : 'span',
33877                     html : this.fieldLabel
33878                 }
33879             ]
33880         };
33881         
33882         if(this.indicatorpos == 'left'){
33883             label.cn.unshift({
33884                 tag : 'i',
33885                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33886                 tooltip : 'This field is required'
33887             });
33888         } else {
33889             label.cn.push({
33890                 tag : 'i',
33891                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33892                 tooltip : 'This field is required'
33893             });
33894         }
33895         
33896         var items = {
33897             tag : 'div',
33898             cls : 'roo-radio-set-items'
33899         };
33900         
33901         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33902         
33903         if (align === 'left' && this.fieldLabel.length) {
33904             
33905             items = {
33906                 cls : "roo-radio-set-right", 
33907                 cn: [
33908                     items
33909                 ]
33910             };
33911             
33912             if(this.labelWidth > 12){
33913                 label.style = "width: " + this.labelWidth + 'px';
33914             }
33915             
33916             if(this.labelWidth < 13 && this.labelmd == 0){
33917                 this.labelmd = this.labelWidth;
33918             }
33919             
33920             if(this.labellg > 0){
33921                 label.cls += ' col-lg-' + this.labellg;
33922                 items.cls += ' col-lg-' + (12 - this.labellg);
33923             }
33924             
33925             if(this.labelmd > 0){
33926                 label.cls += ' col-md-' + this.labelmd;
33927                 items.cls += ' col-md-' + (12 - this.labelmd);
33928             }
33929             
33930             if(this.labelsm > 0){
33931                 label.cls += ' col-sm-' + this.labelsm;
33932                 items.cls += ' col-sm-' + (12 - this.labelsm);
33933             }
33934             
33935             if(this.labelxs > 0){
33936                 label.cls += ' col-xs-' + this.labelxs;
33937                 items.cls += ' col-xs-' + (12 - this.labelxs);
33938             }
33939         }
33940         
33941         var cfg = {
33942             tag : 'div',
33943             cls : 'roo-radio-set',
33944             cn : [
33945                 {
33946                     tag : 'input',
33947                     cls : 'roo-radio-set-input',
33948                     type : 'hidden',
33949                     name : this.name,
33950                     value : this.value ? this.value :  ''
33951                 },
33952                 label,
33953                 items
33954             ]
33955         };
33956         
33957         if(this.weight.length){
33958             cfg.cls += ' roo-radio-' + this.weight;
33959         }
33960         
33961         if(this.inline) {
33962             cfg.cls += ' roo-radio-set-inline';
33963         }
33964         
33965         var settings=this;
33966         ['xs','sm','md','lg'].map(function(size){
33967             if (settings[size]) {
33968                 cfg.cls += ' col-' + size + '-' + settings[size];
33969             }
33970         });
33971         
33972         return cfg;
33973         
33974     },
33975
33976     initEvents : function()
33977     {
33978         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33979         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33980         
33981         if(!this.fieldLabel.length){
33982             this.labelEl.hide();
33983         }
33984         
33985         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33986         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33987         
33988         this.indicator = this.indicatorEl();
33989         
33990         if(this.indicator){
33991             this.indicator.addClass('invisible');
33992         }
33993         
33994         this.originalValue = this.getValue();
33995         
33996     },
33997     
33998     inputEl: function ()
33999     {
34000         return this.el.select('.roo-radio-set-input', true).first();
34001     },
34002     
34003     getChildContainer : function()
34004     {
34005         return this.itemsEl;
34006     },
34007     
34008     register : function(item)
34009     {
34010         this.radioes.push(item);
34011         
34012     },
34013     
34014     validate : function()
34015     {   
34016         if(this.getVisibilityEl().hasClass('hidden')){
34017             return true;
34018         }
34019         
34020         var valid = false;
34021         
34022         Roo.each(this.radioes, function(i){
34023             if(!i.checked){
34024                 return;
34025             }
34026             
34027             valid = true;
34028             return false;
34029         });
34030         
34031         if(this.allowBlank) {
34032             return true;
34033         }
34034         
34035         if(this.disabled || valid){
34036             this.markValid();
34037             return true;
34038         }
34039         
34040         this.markInvalid();
34041         return false;
34042         
34043     },
34044     
34045     markValid : function()
34046     {
34047         if(this.labelEl.isVisible(true)){
34048             this.indicatorEl().removeClass('visible');
34049             this.indicatorEl().addClass('invisible');
34050         }
34051         
34052         this.el.removeClass([this.invalidClass, this.validClass]);
34053         this.el.addClass(this.validClass);
34054         
34055         this.fireEvent('valid', this);
34056     },
34057     
34058     markInvalid : function(msg)
34059     {
34060         if(this.allowBlank || this.disabled){
34061             return;
34062         }
34063         
34064         if(this.labelEl.isVisible(true)){
34065             this.indicatorEl().removeClass('invisible');
34066             this.indicatorEl().addClass('visible');
34067         }
34068         
34069         this.el.removeClass([this.invalidClass, this.validClass]);
34070         this.el.addClass(this.invalidClass);
34071         
34072         this.fireEvent('invalid', this, msg);
34073         
34074     },
34075     
34076     setValue : function(v, suppressEvent)
34077     {   
34078         if(this.value === v){
34079             return;
34080         }
34081         
34082         this.value = v;
34083         
34084         if(this.rendered){
34085             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34086         }
34087         
34088         Roo.each(this.radioes, function(i){
34089             i.checked = false;
34090             i.el.removeClass('checked');
34091         });
34092         
34093         Roo.each(this.radioes, function(i){
34094             
34095             if(i.value === v || i.value.toString() === v.toString()){
34096                 i.checked = true;
34097                 i.el.addClass('checked');
34098                 
34099                 if(suppressEvent !== true){
34100                     this.fireEvent('check', this, i);
34101                 }
34102                 
34103                 return false;
34104             }
34105             
34106         }, this);
34107         
34108         this.validate();
34109     },
34110     
34111     clearInvalid : function(){
34112         
34113         if(!this.el || this.preventMark){
34114             return;
34115         }
34116         
34117         this.el.removeClass([this.invalidClass]);
34118         
34119         this.fireEvent('valid', this);
34120     }
34121     
34122 });
34123
34124 Roo.apply(Roo.bootstrap.RadioSet, {
34125     
34126     groups: {},
34127     
34128     register : function(set)
34129     {
34130         this.groups[set.name] = set;
34131     },
34132     
34133     get: function(name) 
34134     {
34135         if (typeof(this.groups[name]) == 'undefined') {
34136             return false;
34137         }
34138         
34139         return this.groups[name] ;
34140     }
34141     
34142 });
34143 /*
34144  * Based on:
34145  * Ext JS Library 1.1.1
34146  * Copyright(c) 2006-2007, Ext JS, LLC.
34147  *
34148  * Originally Released Under LGPL - original licence link has changed is not relivant.
34149  *
34150  * Fork - LGPL
34151  * <script type="text/javascript">
34152  */
34153
34154
34155 /**
34156  * @class Roo.bootstrap.SplitBar
34157  * @extends Roo.util.Observable
34158  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34159  * <br><br>
34160  * Usage:
34161  * <pre><code>
34162 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34163                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34164 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34165 split.minSize = 100;
34166 split.maxSize = 600;
34167 split.animate = true;
34168 split.on('moved', splitterMoved);
34169 </code></pre>
34170  * @constructor
34171  * Create a new SplitBar
34172  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34173  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34174  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34175  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34176                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34177                         position of the SplitBar).
34178  */
34179 Roo.bootstrap.SplitBar = function(cfg){
34180     
34181     /** @private */
34182     
34183     //{
34184     //  dragElement : elm
34185     //  resizingElement: el,
34186         // optional..
34187     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34188     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34189         // existingProxy ???
34190     //}
34191     
34192     this.el = Roo.get(cfg.dragElement, true);
34193     this.el.dom.unselectable = "on";
34194     /** @private */
34195     this.resizingEl = Roo.get(cfg.resizingElement, true);
34196
34197     /**
34198      * @private
34199      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34200      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34201      * @type Number
34202      */
34203     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34204     
34205     /**
34206      * The minimum size of the resizing element. (Defaults to 0)
34207      * @type Number
34208      */
34209     this.minSize = 0;
34210     
34211     /**
34212      * The maximum size of the resizing element. (Defaults to 2000)
34213      * @type Number
34214      */
34215     this.maxSize = 2000;
34216     
34217     /**
34218      * Whether to animate the transition to the new size
34219      * @type Boolean
34220      */
34221     this.animate = false;
34222     
34223     /**
34224      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34225      * @type Boolean
34226      */
34227     this.useShim = false;
34228     
34229     /** @private */
34230     this.shim = null;
34231     
34232     if(!cfg.existingProxy){
34233         /** @private */
34234         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34235     }else{
34236         this.proxy = Roo.get(cfg.existingProxy).dom;
34237     }
34238     /** @private */
34239     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34240     
34241     /** @private */
34242     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34243     
34244     /** @private */
34245     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34246     
34247     /** @private */
34248     this.dragSpecs = {};
34249     
34250     /**
34251      * @private The adapter to use to positon and resize elements
34252      */
34253     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34254     this.adapter.init(this);
34255     
34256     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34257         /** @private */
34258         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34259         this.el.addClass("roo-splitbar-h");
34260     }else{
34261         /** @private */
34262         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34263         this.el.addClass("roo-splitbar-v");
34264     }
34265     
34266     this.addEvents({
34267         /**
34268          * @event resize
34269          * Fires when the splitter is moved (alias for {@link #event-moved})
34270          * @param {Roo.bootstrap.SplitBar} this
34271          * @param {Number} newSize the new width or height
34272          */
34273         "resize" : true,
34274         /**
34275          * @event moved
34276          * Fires when the splitter is moved
34277          * @param {Roo.bootstrap.SplitBar} this
34278          * @param {Number} newSize the new width or height
34279          */
34280         "moved" : true,
34281         /**
34282          * @event beforeresize
34283          * Fires before the splitter is dragged
34284          * @param {Roo.bootstrap.SplitBar} this
34285          */
34286         "beforeresize" : true,
34287
34288         "beforeapply" : true
34289     });
34290
34291     Roo.util.Observable.call(this);
34292 };
34293
34294 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34295     onStartProxyDrag : function(x, y){
34296         this.fireEvent("beforeresize", this);
34297         if(!this.overlay){
34298             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34299             o.unselectable();
34300             o.enableDisplayMode("block");
34301             // all splitbars share the same overlay
34302             Roo.bootstrap.SplitBar.prototype.overlay = o;
34303         }
34304         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34305         this.overlay.show();
34306         Roo.get(this.proxy).setDisplayed("block");
34307         var size = this.adapter.getElementSize(this);
34308         this.activeMinSize = this.getMinimumSize();;
34309         this.activeMaxSize = this.getMaximumSize();;
34310         var c1 = size - this.activeMinSize;
34311         var c2 = Math.max(this.activeMaxSize - size, 0);
34312         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34313             this.dd.resetConstraints();
34314             this.dd.setXConstraint(
34315                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34316                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34317             );
34318             this.dd.setYConstraint(0, 0);
34319         }else{
34320             this.dd.resetConstraints();
34321             this.dd.setXConstraint(0, 0);
34322             this.dd.setYConstraint(
34323                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34324                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34325             );
34326          }
34327         this.dragSpecs.startSize = size;
34328         this.dragSpecs.startPoint = [x, y];
34329         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34330     },
34331     
34332     /** 
34333      * @private Called after the drag operation by the DDProxy
34334      */
34335     onEndProxyDrag : function(e){
34336         Roo.get(this.proxy).setDisplayed(false);
34337         var endPoint = Roo.lib.Event.getXY(e);
34338         if(this.overlay){
34339             this.overlay.hide();
34340         }
34341         var newSize;
34342         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34343             newSize = this.dragSpecs.startSize + 
34344                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34345                     endPoint[0] - this.dragSpecs.startPoint[0] :
34346                     this.dragSpecs.startPoint[0] - endPoint[0]
34347                 );
34348         }else{
34349             newSize = this.dragSpecs.startSize + 
34350                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34351                     endPoint[1] - this.dragSpecs.startPoint[1] :
34352                     this.dragSpecs.startPoint[1] - endPoint[1]
34353                 );
34354         }
34355         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34356         if(newSize != this.dragSpecs.startSize){
34357             if(this.fireEvent('beforeapply', this, newSize) !== false){
34358                 this.adapter.setElementSize(this, newSize);
34359                 this.fireEvent("moved", this, newSize);
34360                 this.fireEvent("resize", this, newSize);
34361             }
34362         }
34363     },
34364     
34365     /**
34366      * Get the adapter this SplitBar uses
34367      * @return The adapter object
34368      */
34369     getAdapter : function(){
34370         return this.adapter;
34371     },
34372     
34373     /**
34374      * Set the adapter this SplitBar uses
34375      * @param {Object} adapter A SplitBar adapter object
34376      */
34377     setAdapter : function(adapter){
34378         this.adapter = adapter;
34379         this.adapter.init(this);
34380     },
34381     
34382     /**
34383      * Gets the minimum size for the resizing element
34384      * @return {Number} The minimum size
34385      */
34386     getMinimumSize : function(){
34387         return this.minSize;
34388     },
34389     
34390     /**
34391      * Sets the minimum size for the resizing element
34392      * @param {Number} minSize The minimum size
34393      */
34394     setMinimumSize : function(minSize){
34395         this.minSize = minSize;
34396     },
34397     
34398     /**
34399      * Gets the maximum size for the resizing element
34400      * @return {Number} The maximum size
34401      */
34402     getMaximumSize : function(){
34403         return this.maxSize;
34404     },
34405     
34406     /**
34407      * Sets the maximum size for the resizing element
34408      * @param {Number} maxSize The maximum size
34409      */
34410     setMaximumSize : function(maxSize){
34411         this.maxSize = maxSize;
34412     },
34413     
34414     /**
34415      * Sets the initialize size for the resizing element
34416      * @param {Number} size The initial size
34417      */
34418     setCurrentSize : function(size){
34419         var oldAnimate = this.animate;
34420         this.animate = false;
34421         this.adapter.setElementSize(this, size);
34422         this.animate = oldAnimate;
34423     },
34424     
34425     /**
34426      * Destroy this splitbar. 
34427      * @param {Boolean} removeEl True to remove the element
34428      */
34429     destroy : function(removeEl){
34430         if(this.shim){
34431             this.shim.remove();
34432         }
34433         this.dd.unreg();
34434         this.proxy.parentNode.removeChild(this.proxy);
34435         if(removeEl){
34436             this.el.remove();
34437         }
34438     }
34439 });
34440
34441 /**
34442  * @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.
34443  */
34444 Roo.bootstrap.SplitBar.createProxy = function(dir){
34445     var proxy = new Roo.Element(document.createElement("div"));
34446     proxy.unselectable();
34447     var cls = 'roo-splitbar-proxy';
34448     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34449     document.body.appendChild(proxy.dom);
34450     return proxy.dom;
34451 };
34452
34453 /** 
34454  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34455  * Default Adapter. It assumes the splitter and resizing element are not positioned
34456  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34457  */
34458 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34459 };
34460
34461 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34462     // do nothing for now
34463     init : function(s){
34464     
34465     },
34466     /**
34467      * Called before drag operations to get the current size of the resizing element. 
34468      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34469      */
34470      getElementSize : function(s){
34471         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34472             return s.resizingEl.getWidth();
34473         }else{
34474             return s.resizingEl.getHeight();
34475         }
34476     },
34477     
34478     /**
34479      * Called after drag operations to set the size of the resizing element.
34480      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34481      * @param {Number} newSize The new size to set
34482      * @param {Function} onComplete A function to be invoked when resizing is complete
34483      */
34484     setElementSize : function(s, newSize, onComplete){
34485         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34486             if(!s.animate){
34487                 s.resizingEl.setWidth(newSize);
34488                 if(onComplete){
34489                     onComplete(s, newSize);
34490                 }
34491             }else{
34492                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34493             }
34494         }else{
34495             
34496             if(!s.animate){
34497                 s.resizingEl.setHeight(newSize);
34498                 if(onComplete){
34499                     onComplete(s, newSize);
34500                 }
34501             }else{
34502                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34503             }
34504         }
34505     }
34506 };
34507
34508 /** 
34509  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34510  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34511  * Adapter that  moves the splitter element to align with the resized sizing element. 
34512  * Used with an absolute positioned SplitBar.
34513  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34514  * document.body, make sure you assign an id to the body element.
34515  */
34516 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34517     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34518     this.container = Roo.get(container);
34519 };
34520
34521 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34522     init : function(s){
34523         this.basic.init(s);
34524     },
34525     
34526     getElementSize : function(s){
34527         return this.basic.getElementSize(s);
34528     },
34529     
34530     setElementSize : function(s, newSize, onComplete){
34531         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34532     },
34533     
34534     moveSplitter : function(s){
34535         var yes = Roo.bootstrap.SplitBar;
34536         switch(s.placement){
34537             case yes.LEFT:
34538                 s.el.setX(s.resizingEl.getRight());
34539                 break;
34540             case yes.RIGHT:
34541                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34542                 break;
34543             case yes.TOP:
34544                 s.el.setY(s.resizingEl.getBottom());
34545                 break;
34546             case yes.BOTTOM:
34547                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34548                 break;
34549         }
34550     }
34551 };
34552
34553 /**
34554  * Orientation constant - Create a vertical SplitBar
34555  * @static
34556  * @type Number
34557  */
34558 Roo.bootstrap.SplitBar.VERTICAL = 1;
34559
34560 /**
34561  * Orientation constant - Create a horizontal SplitBar
34562  * @static
34563  * @type Number
34564  */
34565 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34566
34567 /**
34568  * Placement constant - The resizing element is to the left of the splitter element
34569  * @static
34570  * @type Number
34571  */
34572 Roo.bootstrap.SplitBar.LEFT = 1;
34573
34574 /**
34575  * Placement constant - The resizing element is to the right of the splitter element
34576  * @static
34577  * @type Number
34578  */
34579 Roo.bootstrap.SplitBar.RIGHT = 2;
34580
34581 /**
34582  * Placement constant - The resizing element is positioned above the splitter element
34583  * @static
34584  * @type Number
34585  */
34586 Roo.bootstrap.SplitBar.TOP = 3;
34587
34588 /**
34589  * Placement constant - The resizing element is positioned under splitter element
34590  * @static
34591  * @type Number
34592  */
34593 Roo.bootstrap.SplitBar.BOTTOM = 4;
34594 Roo.namespace("Roo.bootstrap.layout");/*
34595  * Based on:
34596  * Ext JS Library 1.1.1
34597  * Copyright(c) 2006-2007, Ext JS, LLC.
34598  *
34599  * Originally Released Under LGPL - original licence link has changed is not relivant.
34600  *
34601  * Fork - LGPL
34602  * <script type="text/javascript">
34603  */
34604
34605 /**
34606  * @class Roo.bootstrap.layout.Manager
34607  * @extends Roo.bootstrap.Component
34608  * Base class for layout managers.
34609  */
34610 Roo.bootstrap.layout.Manager = function(config)
34611 {
34612     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34613
34614
34615
34616
34617
34618     /** false to disable window resize monitoring @type Boolean */
34619     this.monitorWindowResize = true;
34620     this.regions = {};
34621     this.addEvents({
34622         /**
34623          * @event layout
34624          * Fires when a layout is performed.
34625          * @param {Roo.LayoutManager} this
34626          */
34627         "layout" : true,
34628         /**
34629          * @event regionresized
34630          * Fires when the user resizes a region.
34631          * @param {Roo.LayoutRegion} region The resized region
34632          * @param {Number} newSize The new size (width for east/west, height for north/south)
34633          */
34634         "regionresized" : true,
34635         /**
34636          * @event regioncollapsed
34637          * Fires when a region is collapsed.
34638          * @param {Roo.LayoutRegion} region The collapsed region
34639          */
34640         "regioncollapsed" : true,
34641         /**
34642          * @event regionexpanded
34643          * Fires when a region is expanded.
34644          * @param {Roo.LayoutRegion} region The expanded region
34645          */
34646         "regionexpanded" : true
34647     });
34648     this.updating = false;
34649
34650     if (config.el) {
34651         this.el = Roo.get(config.el);
34652         this.initEvents();
34653     }
34654
34655 };
34656
34657 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34658
34659
34660     regions : null,
34661
34662     monitorWindowResize : true,
34663
34664
34665     updating : false,
34666
34667
34668     onRender : function(ct, position)
34669     {
34670         if(!this.el){
34671             this.el = Roo.get(ct);
34672             this.initEvents();
34673         }
34674         //this.fireEvent('render',this);
34675     },
34676
34677
34678     initEvents: function()
34679     {
34680
34681
34682         // ie scrollbar fix
34683         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34684             document.body.scroll = "no";
34685         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34686             this.el.position('relative');
34687         }
34688         this.id = this.el.id;
34689         this.el.addClass("roo-layout-container");
34690         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34691         if(this.el.dom != document.body ) {
34692             this.el.on('resize', this.layout,this);
34693             this.el.on('show', this.layout,this);
34694         }
34695
34696     },
34697
34698     /**
34699      * Returns true if this layout is currently being updated
34700      * @return {Boolean}
34701      */
34702     isUpdating : function(){
34703         return this.updating;
34704     },
34705
34706     /**
34707      * Suspend the LayoutManager from doing auto-layouts while
34708      * making multiple add or remove calls
34709      */
34710     beginUpdate : function(){
34711         this.updating = true;
34712     },
34713
34714     /**
34715      * Restore auto-layouts and optionally disable the manager from performing a layout
34716      * @param {Boolean} noLayout true to disable a layout update
34717      */
34718     endUpdate : function(noLayout){
34719         this.updating = false;
34720         if(!noLayout){
34721             this.layout();
34722         }
34723     },
34724
34725     layout: function(){
34726         // abstract...
34727     },
34728
34729     onRegionResized : function(region, newSize){
34730         this.fireEvent("regionresized", region, newSize);
34731         this.layout();
34732     },
34733
34734     onRegionCollapsed : function(region){
34735         this.fireEvent("regioncollapsed", region);
34736     },
34737
34738     onRegionExpanded : function(region){
34739         this.fireEvent("regionexpanded", region);
34740     },
34741
34742     /**
34743      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34744      * performs box-model adjustments.
34745      * @return {Object} The size as an object {width: (the width), height: (the height)}
34746      */
34747     getViewSize : function()
34748     {
34749         var size;
34750         if(this.el.dom != document.body){
34751             size = this.el.getSize();
34752         }else{
34753             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34754         }
34755         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34756         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34757         return size;
34758     },
34759
34760     /**
34761      * Returns the Element this layout is bound to.
34762      * @return {Roo.Element}
34763      */
34764     getEl : function(){
34765         return this.el;
34766     },
34767
34768     /**
34769      * Returns the specified region.
34770      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34771      * @return {Roo.LayoutRegion}
34772      */
34773     getRegion : function(target){
34774         return this.regions[target.toLowerCase()];
34775     },
34776
34777     onWindowResize : function(){
34778         if(this.monitorWindowResize){
34779             this.layout();
34780         }
34781     }
34782 });
34783 /*
34784  * Based on:
34785  * Ext JS Library 1.1.1
34786  * Copyright(c) 2006-2007, Ext JS, LLC.
34787  *
34788  * Originally Released Under LGPL - original licence link has changed is not relivant.
34789  *
34790  * Fork - LGPL
34791  * <script type="text/javascript">
34792  */
34793 /**
34794  * @class Roo.bootstrap.layout.Border
34795  * @extends Roo.bootstrap.layout.Manager
34796  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34797  * please see: examples/bootstrap/nested.html<br><br>
34798  
34799 <b>The container the layout is rendered into can be either the body element or any other element.
34800 If it is not the body element, the container needs to either be an absolute positioned element,
34801 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34802 the container size if it is not the body element.</b>
34803
34804 * @constructor
34805 * Create a new Border
34806 * @param {Object} config Configuration options
34807  */
34808 Roo.bootstrap.layout.Border = function(config){
34809     config = config || {};
34810     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34811     
34812     
34813     
34814     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34815         if(config[region]){
34816             config[region].region = region;
34817             this.addRegion(config[region]);
34818         }
34819     },this);
34820     
34821 };
34822
34823 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34824
34825 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34826     /**
34827      * Creates and adds a new region if it doesn't already exist.
34828      * @param {String} target The target region key (north, south, east, west or center).
34829      * @param {Object} config The regions config object
34830      * @return {BorderLayoutRegion} The new region
34831      */
34832     addRegion : function(config)
34833     {
34834         if(!this.regions[config.region]){
34835             var r = this.factory(config);
34836             this.bindRegion(r);
34837         }
34838         return this.regions[config.region];
34839     },
34840
34841     // private (kinda)
34842     bindRegion : function(r){
34843         this.regions[r.config.region] = r;
34844         
34845         r.on("visibilitychange",    this.layout, this);
34846         r.on("paneladded",          this.layout, this);
34847         r.on("panelremoved",        this.layout, this);
34848         r.on("invalidated",         this.layout, this);
34849         r.on("resized",             this.onRegionResized, this);
34850         r.on("collapsed",           this.onRegionCollapsed, this);
34851         r.on("expanded",            this.onRegionExpanded, this);
34852     },
34853
34854     /**
34855      * Performs a layout update.
34856      */
34857     layout : function()
34858     {
34859         if(this.updating) {
34860             return;
34861         }
34862         
34863         // render all the rebions if they have not been done alreayd?
34864         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34865             if(this.regions[region] && !this.regions[region].bodyEl){
34866                 this.regions[region].onRender(this.el)
34867             }
34868         },this);
34869         
34870         var size = this.getViewSize();
34871         var w = size.width;
34872         var h = size.height;
34873         var centerW = w;
34874         var centerH = h;
34875         var centerY = 0;
34876         var centerX = 0;
34877         //var x = 0, y = 0;
34878
34879         var rs = this.regions;
34880         var north = rs["north"];
34881         var south = rs["south"]; 
34882         var west = rs["west"];
34883         var east = rs["east"];
34884         var center = rs["center"];
34885         //if(this.hideOnLayout){ // not supported anymore
34886             //c.el.setStyle("display", "none");
34887         //}
34888         if(north && north.isVisible()){
34889             var b = north.getBox();
34890             var m = north.getMargins();
34891             b.width = w - (m.left+m.right);
34892             b.x = m.left;
34893             b.y = m.top;
34894             centerY = b.height + b.y + m.bottom;
34895             centerH -= centerY;
34896             north.updateBox(this.safeBox(b));
34897         }
34898         if(south && south.isVisible()){
34899             var b = south.getBox();
34900             var m = south.getMargins();
34901             b.width = w - (m.left+m.right);
34902             b.x = m.left;
34903             var totalHeight = (b.height + m.top + m.bottom);
34904             b.y = h - totalHeight + m.top;
34905             centerH -= totalHeight;
34906             south.updateBox(this.safeBox(b));
34907         }
34908         if(west && west.isVisible()){
34909             var b = west.getBox();
34910             var m = west.getMargins();
34911             b.height = centerH - (m.top+m.bottom);
34912             b.x = m.left;
34913             b.y = centerY + m.top;
34914             var totalWidth = (b.width + m.left + m.right);
34915             centerX += totalWidth;
34916             centerW -= totalWidth;
34917             west.updateBox(this.safeBox(b));
34918         }
34919         if(east && east.isVisible()){
34920             var b = east.getBox();
34921             var m = east.getMargins();
34922             b.height = centerH - (m.top+m.bottom);
34923             var totalWidth = (b.width + m.left + m.right);
34924             b.x = w - totalWidth + m.left;
34925             b.y = centerY + m.top;
34926             centerW -= totalWidth;
34927             east.updateBox(this.safeBox(b));
34928         }
34929         if(center){
34930             var m = center.getMargins();
34931             var centerBox = {
34932                 x: centerX + m.left,
34933                 y: centerY + m.top,
34934                 width: centerW - (m.left+m.right),
34935                 height: centerH - (m.top+m.bottom)
34936             };
34937             //if(this.hideOnLayout){
34938                 //center.el.setStyle("display", "block");
34939             //}
34940             center.updateBox(this.safeBox(centerBox));
34941         }
34942         this.el.repaint();
34943         this.fireEvent("layout", this);
34944     },
34945
34946     // private
34947     safeBox : function(box){
34948         box.width = Math.max(0, box.width);
34949         box.height = Math.max(0, box.height);
34950         return box;
34951     },
34952
34953     /**
34954      * Adds a ContentPanel (or subclass) to this layout.
34955      * @param {String} target The target region key (north, south, east, west or center).
34956      * @param {Roo.ContentPanel} panel The panel to add
34957      * @return {Roo.ContentPanel} The added panel
34958      */
34959     add : function(target, panel){
34960          
34961         target = target.toLowerCase();
34962         return this.regions[target].add(panel);
34963     },
34964
34965     /**
34966      * Remove a ContentPanel (or subclass) to this layout.
34967      * @param {String} target The target region key (north, south, east, west or center).
34968      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34969      * @return {Roo.ContentPanel} The removed panel
34970      */
34971     remove : function(target, panel){
34972         target = target.toLowerCase();
34973         return this.regions[target].remove(panel);
34974     },
34975
34976     /**
34977      * Searches all regions for a panel with the specified id
34978      * @param {String} panelId
34979      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34980      */
34981     findPanel : function(panelId){
34982         var rs = this.regions;
34983         for(var target in rs){
34984             if(typeof rs[target] != "function"){
34985                 var p = rs[target].getPanel(panelId);
34986                 if(p){
34987                     return p;
34988                 }
34989             }
34990         }
34991         return null;
34992     },
34993
34994     /**
34995      * Searches all regions for a panel with the specified id and activates (shows) it.
34996      * @param {String/ContentPanel} panelId The panels id or the panel itself
34997      * @return {Roo.ContentPanel} The shown panel or null
34998      */
34999     showPanel : function(panelId) {
35000       var rs = this.regions;
35001       for(var target in rs){
35002          var r = rs[target];
35003          if(typeof r != "function"){
35004             if(r.hasPanel(panelId)){
35005                return r.showPanel(panelId);
35006             }
35007          }
35008       }
35009       return null;
35010    },
35011
35012    /**
35013      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35014      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35015      */
35016    /*
35017     restoreState : function(provider){
35018         if(!provider){
35019             provider = Roo.state.Manager;
35020         }
35021         var sm = new Roo.LayoutStateManager();
35022         sm.init(this, provider);
35023     },
35024 */
35025  
35026  
35027     /**
35028      * Adds a xtype elements to the layout.
35029      * <pre><code>
35030
35031 layout.addxtype({
35032        xtype : 'ContentPanel',
35033        region: 'west',
35034        items: [ .... ]
35035    }
35036 );
35037
35038 layout.addxtype({
35039         xtype : 'NestedLayoutPanel',
35040         region: 'west',
35041         layout: {
35042            center: { },
35043            west: { }   
35044         },
35045         items : [ ... list of content panels or nested layout panels.. ]
35046    }
35047 );
35048 </code></pre>
35049      * @param {Object} cfg Xtype definition of item to add.
35050      */
35051     addxtype : function(cfg)
35052     {
35053         // basically accepts a pannel...
35054         // can accept a layout region..!?!?
35055         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35056         
35057         
35058         // theory?  children can only be panels??
35059         
35060         //if (!cfg.xtype.match(/Panel$/)) {
35061         //    return false;
35062         //}
35063         var ret = false;
35064         
35065         if (typeof(cfg.region) == 'undefined') {
35066             Roo.log("Failed to add Panel, region was not set");
35067             Roo.log(cfg);
35068             return false;
35069         }
35070         var region = cfg.region;
35071         delete cfg.region;
35072         
35073           
35074         var xitems = [];
35075         if (cfg.items) {
35076             xitems = cfg.items;
35077             delete cfg.items;
35078         }
35079         var nb = false;
35080         
35081         switch(cfg.xtype) 
35082         {
35083             case 'Content':  // ContentPanel (el, cfg)
35084             case 'Scroll':  // ContentPanel (el, cfg)
35085             case 'View': 
35086                 cfg.autoCreate = true;
35087                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35088                 //} else {
35089                 //    var el = this.el.createChild();
35090                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35091                 //}
35092                 
35093                 this.add(region, ret);
35094                 break;
35095             
35096             /*
35097             case 'TreePanel': // our new panel!
35098                 cfg.el = this.el.createChild();
35099                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35100                 this.add(region, ret);
35101                 break;
35102             */
35103             
35104             case 'Nest': 
35105                 // create a new Layout (which is  a Border Layout...
35106                 
35107                 var clayout = cfg.layout;
35108                 clayout.el  = this.el.createChild();
35109                 clayout.items   = clayout.items  || [];
35110                 
35111                 delete cfg.layout;
35112                 
35113                 // replace this exitems with the clayout ones..
35114                 xitems = clayout.items;
35115                  
35116                 // force background off if it's in center...
35117                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35118                     cfg.background = false;
35119                 }
35120                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35121                 
35122                 
35123                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35124                 //console.log('adding nested layout panel '  + cfg.toSource());
35125                 this.add(region, ret);
35126                 nb = {}; /// find first...
35127                 break;
35128             
35129             case 'Grid':
35130                 
35131                 // needs grid and region
35132                 
35133                 //var el = this.getRegion(region).el.createChild();
35134                 /*
35135                  *var el = this.el.createChild();
35136                 // create the grid first...
35137                 cfg.grid.container = el;
35138                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35139                 */
35140                 
35141                 if (region == 'center' && this.active ) {
35142                     cfg.background = false;
35143                 }
35144                 
35145                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35146                 
35147                 this.add(region, ret);
35148                 /*
35149                 if (cfg.background) {
35150                     // render grid on panel activation (if panel background)
35151                     ret.on('activate', function(gp) {
35152                         if (!gp.grid.rendered) {
35153                     //        gp.grid.render(el);
35154                         }
35155                     });
35156                 } else {
35157                   //  cfg.grid.render(el);
35158                 }
35159                 */
35160                 break;
35161            
35162            
35163             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35164                 // it was the old xcomponent building that caused this before.
35165                 // espeically if border is the top element in the tree.
35166                 ret = this;
35167                 break; 
35168                 
35169                     
35170                 
35171                 
35172                 
35173             default:
35174                 /*
35175                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35176                     
35177                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35178                     this.add(region, ret);
35179                 } else {
35180                 */
35181                     Roo.log(cfg);
35182                     throw "Can not add '" + cfg.xtype + "' to Border";
35183                     return null;
35184              
35185                                 
35186              
35187         }
35188         this.beginUpdate();
35189         // add children..
35190         var region = '';
35191         var abn = {};
35192         Roo.each(xitems, function(i)  {
35193             region = nb && i.region ? i.region : false;
35194             
35195             var add = ret.addxtype(i);
35196            
35197             if (region) {
35198                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35199                 if (!i.background) {
35200                     abn[region] = nb[region] ;
35201                 }
35202             }
35203             
35204         });
35205         this.endUpdate();
35206
35207         // make the last non-background panel active..
35208         //if (nb) { Roo.log(abn); }
35209         if (nb) {
35210             
35211             for(var r in abn) {
35212                 region = this.getRegion(r);
35213                 if (region) {
35214                     // tried using nb[r], but it does not work..
35215                      
35216                     region.showPanel(abn[r]);
35217                    
35218                 }
35219             }
35220         }
35221         return ret;
35222         
35223     },
35224     
35225     
35226 // private
35227     factory : function(cfg)
35228     {
35229         
35230         var validRegions = Roo.bootstrap.layout.Border.regions;
35231
35232         var target = cfg.region;
35233         cfg.mgr = this;
35234         
35235         var r = Roo.bootstrap.layout;
35236         Roo.log(target);
35237         switch(target){
35238             case "north":
35239                 return new r.North(cfg);
35240             case "south":
35241                 return new r.South(cfg);
35242             case "east":
35243                 return new r.East(cfg);
35244             case "west":
35245                 return new r.West(cfg);
35246             case "center":
35247                 return new r.Center(cfg);
35248         }
35249         throw 'Layout region "'+target+'" not supported.';
35250     }
35251     
35252     
35253 });
35254  /*
35255  * Based on:
35256  * Ext JS Library 1.1.1
35257  * Copyright(c) 2006-2007, Ext JS, LLC.
35258  *
35259  * Originally Released Under LGPL - original licence link has changed is not relivant.
35260  *
35261  * Fork - LGPL
35262  * <script type="text/javascript">
35263  */
35264  
35265 /**
35266  * @class Roo.bootstrap.layout.Basic
35267  * @extends Roo.util.Observable
35268  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35269  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35270  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35271  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35272  * @cfg {string}   region  the region that it inhabits..
35273  * @cfg {bool}   skipConfig skip config?
35274  * 
35275
35276  */
35277 Roo.bootstrap.layout.Basic = function(config){
35278     
35279     this.mgr = config.mgr;
35280     
35281     this.position = config.region;
35282     
35283     var skipConfig = config.skipConfig;
35284     
35285     this.events = {
35286         /**
35287          * @scope Roo.BasicLayoutRegion
35288          */
35289         
35290         /**
35291          * @event beforeremove
35292          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35293          * @param {Roo.LayoutRegion} this
35294          * @param {Roo.ContentPanel} panel The panel
35295          * @param {Object} e The cancel event object
35296          */
35297         "beforeremove" : true,
35298         /**
35299          * @event invalidated
35300          * Fires when the layout for this region is changed.
35301          * @param {Roo.LayoutRegion} this
35302          */
35303         "invalidated" : true,
35304         /**
35305          * @event visibilitychange
35306          * Fires when this region is shown or hidden 
35307          * @param {Roo.LayoutRegion} this
35308          * @param {Boolean} visibility true or false
35309          */
35310         "visibilitychange" : true,
35311         /**
35312          * @event paneladded
35313          * Fires when a panel is added. 
35314          * @param {Roo.LayoutRegion} this
35315          * @param {Roo.ContentPanel} panel The panel
35316          */
35317         "paneladded" : true,
35318         /**
35319          * @event panelremoved
35320          * Fires when a panel is removed. 
35321          * @param {Roo.LayoutRegion} this
35322          * @param {Roo.ContentPanel} panel The panel
35323          */
35324         "panelremoved" : true,
35325         /**
35326          * @event beforecollapse
35327          * Fires when this region before collapse.
35328          * @param {Roo.LayoutRegion} this
35329          */
35330         "beforecollapse" : true,
35331         /**
35332          * @event collapsed
35333          * Fires when this region is collapsed.
35334          * @param {Roo.LayoutRegion} this
35335          */
35336         "collapsed" : true,
35337         /**
35338          * @event expanded
35339          * Fires when this region is expanded.
35340          * @param {Roo.LayoutRegion} this
35341          */
35342         "expanded" : true,
35343         /**
35344          * @event slideshow
35345          * Fires when this region is slid into view.
35346          * @param {Roo.LayoutRegion} this
35347          */
35348         "slideshow" : true,
35349         /**
35350          * @event slidehide
35351          * Fires when this region slides out of view. 
35352          * @param {Roo.LayoutRegion} this
35353          */
35354         "slidehide" : true,
35355         /**
35356          * @event panelactivated
35357          * Fires when a panel is activated. 
35358          * @param {Roo.LayoutRegion} this
35359          * @param {Roo.ContentPanel} panel The activated panel
35360          */
35361         "panelactivated" : true,
35362         /**
35363          * @event resized
35364          * Fires when the user resizes this region. 
35365          * @param {Roo.LayoutRegion} this
35366          * @param {Number} newSize The new size (width for east/west, height for north/south)
35367          */
35368         "resized" : true
35369     };
35370     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35371     this.panels = new Roo.util.MixedCollection();
35372     this.panels.getKey = this.getPanelId.createDelegate(this);
35373     this.box = null;
35374     this.activePanel = null;
35375     // ensure listeners are added...
35376     
35377     if (config.listeners || config.events) {
35378         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35379             listeners : config.listeners || {},
35380             events : config.events || {}
35381         });
35382     }
35383     
35384     if(skipConfig !== true){
35385         this.applyConfig(config);
35386     }
35387 };
35388
35389 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35390 {
35391     getPanelId : function(p){
35392         return p.getId();
35393     },
35394     
35395     applyConfig : function(config){
35396         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35397         this.config = config;
35398         
35399     },
35400     
35401     /**
35402      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35403      * the width, for horizontal (north, south) the height.
35404      * @param {Number} newSize The new width or height
35405      */
35406     resizeTo : function(newSize){
35407         var el = this.el ? this.el :
35408                  (this.activePanel ? this.activePanel.getEl() : null);
35409         if(el){
35410             switch(this.position){
35411                 case "east":
35412                 case "west":
35413                     el.setWidth(newSize);
35414                     this.fireEvent("resized", this, newSize);
35415                 break;
35416                 case "north":
35417                 case "south":
35418                     el.setHeight(newSize);
35419                     this.fireEvent("resized", this, newSize);
35420                 break;                
35421             }
35422         }
35423     },
35424     
35425     getBox : function(){
35426         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35427     },
35428     
35429     getMargins : function(){
35430         return this.margins;
35431     },
35432     
35433     updateBox : function(box){
35434         this.box = box;
35435         var el = this.activePanel.getEl();
35436         el.dom.style.left = box.x + "px";
35437         el.dom.style.top = box.y + "px";
35438         this.activePanel.setSize(box.width, box.height);
35439     },
35440     
35441     /**
35442      * Returns the container element for this region.
35443      * @return {Roo.Element}
35444      */
35445     getEl : function(){
35446         return this.activePanel;
35447     },
35448     
35449     /**
35450      * Returns true if this region is currently visible.
35451      * @return {Boolean}
35452      */
35453     isVisible : function(){
35454         return this.activePanel ? true : false;
35455     },
35456     
35457     setActivePanel : function(panel){
35458         panel = this.getPanel(panel);
35459         if(this.activePanel && this.activePanel != panel){
35460             this.activePanel.setActiveState(false);
35461             this.activePanel.getEl().setLeftTop(-10000,-10000);
35462         }
35463         this.activePanel = panel;
35464         panel.setActiveState(true);
35465         if(this.box){
35466             panel.setSize(this.box.width, this.box.height);
35467         }
35468         this.fireEvent("panelactivated", this, panel);
35469         this.fireEvent("invalidated");
35470     },
35471     
35472     /**
35473      * Show the specified panel.
35474      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35475      * @return {Roo.ContentPanel} The shown panel or null
35476      */
35477     showPanel : function(panel){
35478         panel = this.getPanel(panel);
35479         if(panel){
35480             this.setActivePanel(panel);
35481         }
35482         return panel;
35483     },
35484     
35485     /**
35486      * Get the active panel for this region.
35487      * @return {Roo.ContentPanel} The active panel or null
35488      */
35489     getActivePanel : function(){
35490         return this.activePanel;
35491     },
35492     
35493     /**
35494      * Add the passed ContentPanel(s)
35495      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35496      * @return {Roo.ContentPanel} The panel added (if only one was added)
35497      */
35498     add : function(panel){
35499         if(arguments.length > 1){
35500             for(var i = 0, len = arguments.length; i < len; i++) {
35501                 this.add(arguments[i]);
35502             }
35503             return null;
35504         }
35505         if(this.hasPanel(panel)){
35506             this.showPanel(panel);
35507             return panel;
35508         }
35509         var el = panel.getEl();
35510         if(el.dom.parentNode != this.mgr.el.dom){
35511             this.mgr.el.dom.appendChild(el.dom);
35512         }
35513         if(panel.setRegion){
35514             panel.setRegion(this);
35515         }
35516         this.panels.add(panel);
35517         el.setStyle("position", "absolute");
35518         if(!panel.background){
35519             this.setActivePanel(panel);
35520             if(this.config.initialSize && this.panels.getCount()==1){
35521                 this.resizeTo(this.config.initialSize);
35522             }
35523         }
35524         this.fireEvent("paneladded", this, panel);
35525         return panel;
35526     },
35527     
35528     /**
35529      * Returns true if the panel is in this region.
35530      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35531      * @return {Boolean}
35532      */
35533     hasPanel : function(panel){
35534         if(typeof panel == "object"){ // must be panel obj
35535             panel = panel.getId();
35536         }
35537         return this.getPanel(panel) ? true : false;
35538     },
35539     
35540     /**
35541      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35542      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35543      * @param {Boolean} preservePanel Overrides the config preservePanel option
35544      * @return {Roo.ContentPanel} The panel that was removed
35545      */
35546     remove : function(panel, preservePanel){
35547         panel = this.getPanel(panel);
35548         if(!panel){
35549             return null;
35550         }
35551         var e = {};
35552         this.fireEvent("beforeremove", this, panel, e);
35553         if(e.cancel === true){
35554             return null;
35555         }
35556         var panelId = panel.getId();
35557         this.panels.removeKey(panelId);
35558         return panel;
35559     },
35560     
35561     /**
35562      * Returns the panel specified or null if it's not in this region.
35563      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35564      * @return {Roo.ContentPanel}
35565      */
35566     getPanel : function(id){
35567         if(typeof id == "object"){ // must be panel obj
35568             return id;
35569         }
35570         return this.panels.get(id);
35571     },
35572     
35573     /**
35574      * Returns this regions position (north/south/east/west/center).
35575      * @return {String} 
35576      */
35577     getPosition: function(){
35578         return this.position;    
35579     }
35580 });/*
35581  * Based on:
35582  * Ext JS Library 1.1.1
35583  * Copyright(c) 2006-2007, Ext JS, LLC.
35584  *
35585  * Originally Released Under LGPL - original licence link has changed is not relivant.
35586  *
35587  * Fork - LGPL
35588  * <script type="text/javascript">
35589  */
35590  
35591 /**
35592  * @class Roo.bootstrap.layout.Region
35593  * @extends Roo.bootstrap.layout.Basic
35594  * This class represents a region in a layout manager.
35595  
35596  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35597  * @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})
35598  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35599  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35600  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35601  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35602  * @cfg {String}    title           The title for the region (overrides panel titles)
35603  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35604  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35605  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35606  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35607  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35608  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35609  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35610  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35611  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35612  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35613
35614  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35615  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35616  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35617  * @cfg {Number}    width           For East/West panels
35618  * @cfg {Number}    height          For North/South panels
35619  * @cfg {Boolean}   split           To show the splitter
35620  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35621  * 
35622  * @cfg {string}   cls             Extra CSS classes to add to region
35623  * 
35624  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35625  * @cfg {string}   region  the region that it inhabits..
35626  *
35627
35628  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35629  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35630
35631  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35632  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35633  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35634  */
35635 Roo.bootstrap.layout.Region = function(config)
35636 {
35637     this.applyConfig(config);
35638
35639     var mgr = config.mgr;
35640     var pos = config.region;
35641     config.skipConfig = true;
35642     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35643     
35644     if (mgr.el) {
35645         this.onRender(mgr.el);   
35646     }
35647      
35648     this.visible = true;
35649     this.collapsed = false;
35650     this.unrendered_panels = [];
35651 };
35652
35653 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35654
35655     position: '', // set by wrapper (eg. north/south etc..)
35656     unrendered_panels : null,  // unrendered panels.
35657     createBody : function(){
35658         /** This region's body element 
35659         * @type Roo.Element */
35660         this.bodyEl = this.el.createChild({
35661                 tag: "div",
35662                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35663         });
35664     },
35665
35666     onRender: function(ctr, pos)
35667     {
35668         var dh = Roo.DomHelper;
35669         /** This region's container element 
35670         * @type Roo.Element */
35671         this.el = dh.append(ctr.dom, {
35672                 tag: "div",
35673                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35674             }, true);
35675         /** This region's title element 
35676         * @type Roo.Element */
35677     
35678         this.titleEl = dh.append(this.el.dom,
35679             {
35680                     tag: "div",
35681                     unselectable: "on",
35682                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35683                     children:[
35684                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35685                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35686                     ]}, true);
35687         
35688         this.titleEl.enableDisplayMode();
35689         /** This region's title text element 
35690         * @type HTMLElement */
35691         this.titleTextEl = this.titleEl.dom.firstChild;
35692         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35693         /*
35694         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35695         this.closeBtn.enableDisplayMode();
35696         this.closeBtn.on("click", this.closeClicked, this);
35697         this.closeBtn.hide();
35698     */
35699         this.createBody(this.config);
35700         if(this.config.hideWhenEmpty){
35701             this.hide();
35702             this.on("paneladded", this.validateVisibility, this);
35703             this.on("panelremoved", this.validateVisibility, this);
35704         }
35705         if(this.autoScroll){
35706             this.bodyEl.setStyle("overflow", "auto");
35707         }else{
35708             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35709         }
35710         //if(c.titlebar !== false){
35711             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35712                 this.titleEl.hide();
35713             }else{
35714                 this.titleEl.show();
35715                 if(this.config.title){
35716                     this.titleTextEl.innerHTML = this.config.title;
35717                 }
35718             }
35719         //}
35720         if(this.config.collapsed){
35721             this.collapse(true);
35722         }
35723         if(this.config.hidden){
35724             this.hide();
35725         }
35726         
35727         if (this.unrendered_panels && this.unrendered_panels.length) {
35728             for (var i =0;i< this.unrendered_panels.length; i++) {
35729                 this.add(this.unrendered_panels[i]);
35730             }
35731             this.unrendered_panels = null;
35732             
35733         }
35734         
35735     },
35736     
35737     applyConfig : function(c)
35738     {
35739         /*
35740          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35741             var dh = Roo.DomHelper;
35742             if(c.titlebar !== false){
35743                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35744                 this.collapseBtn.on("click", this.collapse, this);
35745                 this.collapseBtn.enableDisplayMode();
35746                 /*
35747                 if(c.showPin === true || this.showPin){
35748                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35749                     this.stickBtn.enableDisplayMode();
35750                     this.stickBtn.on("click", this.expand, this);
35751                     this.stickBtn.hide();
35752                 }
35753                 
35754             }
35755             */
35756             /** This region's collapsed element
35757             * @type Roo.Element */
35758             /*
35759              *
35760             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35761                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35762             ]}, true);
35763             
35764             if(c.floatable !== false){
35765                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35766                this.collapsedEl.on("click", this.collapseClick, this);
35767             }
35768
35769             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35770                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35771                    id: "message", unselectable: "on", style:{"float":"left"}});
35772                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35773              }
35774             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35775             this.expandBtn.on("click", this.expand, this);
35776             
35777         }
35778         
35779         if(this.collapseBtn){
35780             this.collapseBtn.setVisible(c.collapsible == true);
35781         }
35782         
35783         this.cmargins = c.cmargins || this.cmargins ||
35784                          (this.position == "west" || this.position == "east" ?
35785                              {top: 0, left: 2, right:2, bottom: 0} :
35786                              {top: 2, left: 0, right:0, bottom: 2});
35787         */
35788         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35789         
35790         
35791         this.bottomTabs = c.tabPosition != "top";
35792         
35793         this.autoScroll = c.autoScroll || false;
35794         
35795         
35796        
35797         
35798         this.duration = c.duration || .30;
35799         this.slideDuration = c.slideDuration || .45;
35800         this.config = c;
35801        
35802     },
35803     /**
35804      * Returns true if this region is currently visible.
35805      * @return {Boolean}
35806      */
35807     isVisible : function(){
35808         return this.visible;
35809     },
35810
35811     /**
35812      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35813      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35814      */
35815     //setCollapsedTitle : function(title){
35816     //    title = title || "&#160;";
35817      //   if(this.collapsedTitleTextEl){
35818       //      this.collapsedTitleTextEl.innerHTML = title;
35819        // }
35820     //},
35821
35822     getBox : function(){
35823         var b;
35824       //  if(!this.collapsed){
35825             b = this.el.getBox(false, true);
35826        // }else{
35827           //  b = this.collapsedEl.getBox(false, true);
35828         //}
35829         return b;
35830     },
35831
35832     getMargins : function(){
35833         return this.margins;
35834         //return this.collapsed ? this.cmargins : this.margins;
35835     },
35836 /*
35837     highlight : function(){
35838         this.el.addClass("x-layout-panel-dragover");
35839     },
35840
35841     unhighlight : function(){
35842         this.el.removeClass("x-layout-panel-dragover");
35843     },
35844 */
35845     updateBox : function(box)
35846     {
35847         if (!this.bodyEl) {
35848             return; // not rendered yet..
35849         }
35850         
35851         this.box = box;
35852         if(!this.collapsed){
35853             this.el.dom.style.left = box.x + "px";
35854             this.el.dom.style.top = box.y + "px";
35855             this.updateBody(box.width, box.height);
35856         }else{
35857             this.collapsedEl.dom.style.left = box.x + "px";
35858             this.collapsedEl.dom.style.top = box.y + "px";
35859             this.collapsedEl.setSize(box.width, box.height);
35860         }
35861         if(this.tabs){
35862             this.tabs.autoSizeTabs();
35863         }
35864     },
35865
35866     updateBody : function(w, h)
35867     {
35868         if(w !== null){
35869             this.el.setWidth(w);
35870             w -= this.el.getBorderWidth("rl");
35871             if(this.config.adjustments){
35872                 w += this.config.adjustments[0];
35873             }
35874         }
35875         if(h !== null && h > 0){
35876             this.el.setHeight(h);
35877             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35878             h -= this.el.getBorderWidth("tb");
35879             if(this.config.adjustments){
35880                 h += this.config.adjustments[1];
35881             }
35882             this.bodyEl.setHeight(h);
35883             if(this.tabs){
35884                 h = this.tabs.syncHeight(h);
35885             }
35886         }
35887         if(this.panelSize){
35888             w = w !== null ? w : this.panelSize.width;
35889             h = h !== null ? h : this.panelSize.height;
35890         }
35891         if(this.activePanel){
35892             var el = this.activePanel.getEl();
35893             w = w !== null ? w : el.getWidth();
35894             h = h !== null ? h : el.getHeight();
35895             this.panelSize = {width: w, height: h};
35896             this.activePanel.setSize(w, h);
35897         }
35898         if(Roo.isIE && this.tabs){
35899             this.tabs.el.repaint();
35900         }
35901     },
35902
35903     /**
35904      * Returns the container element for this region.
35905      * @return {Roo.Element}
35906      */
35907     getEl : function(){
35908         return this.el;
35909     },
35910
35911     /**
35912      * Hides this region.
35913      */
35914     hide : function(){
35915         //if(!this.collapsed){
35916             this.el.dom.style.left = "-2000px";
35917             this.el.hide();
35918         //}else{
35919          //   this.collapsedEl.dom.style.left = "-2000px";
35920          //   this.collapsedEl.hide();
35921        // }
35922         this.visible = false;
35923         this.fireEvent("visibilitychange", this, false);
35924     },
35925
35926     /**
35927      * Shows this region if it was previously hidden.
35928      */
35929     show : function(){
35930         //if(!this.collapsed){
35931             this.el.show();
35932         //}else{
35933         //    this.collapsedEl.show();
35934        // }
35935         this.visible = true;
35936         this.fireEvent("visibilitychange", this, true);
35937     },
35938 /*
35939     closeClicked : function(){
35940         if(this.activePanel){
35941             this.remove(this.activePanel);
35942         }
35943     },
35944
35945     collapseClick : function(e){
35946         if(this.isSlid){
35947            e.stopPropagation();
35948            this.slideIn();
35949         }else{
35950            e.stopPropagation();
35951            this.slideOut();
35952         }
35953     },
35954 */
35955     /**
35956      * Collapses this region.
35957      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35958      */
35959     /*
35960     collapse : function(skipAnim, skipCheck = false){
35961         if(this.collapsed) {
35962             return;
35963         }
35964         
35965         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35966             
35967             this.collapsed = true;
35968             if(this.split){
35969                 this.split.el.hide();
35970             }
35971             if(this.config.animate && skipAnim !== true){
35972                 this.fireEvent("invalidated", this);
35973                 this.animateCollapse();
35974             }else{
35975                 this.el.setLocation(-20000,-20000);
35976                 this.el.hide();
35977                 this.collapsedEl.show();
35978                 this.fireEvent("collapsed", this);
35979                 this.fireEvent("invalidated", this);
35980             }
35981         }
35982         
35983     },
35984 */
35985     animateCollapse : function(){
35986         // overridden
35987     },
35988
35989     /**
35990      * Expands this region if it was previously collapsed.
35991      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35992      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35993      */
35994     /*
35995     expand : function(e, skipAnim){
35996         if(e) {
35997             e.stopPropagation();
35998         }
35999         if(!this.collapsed || this.el.hasActiveFx()) {
36000             return;
36001         }
36002         if(this.isSlid){
36003             this.afterSlideIn();
36004             skipAnim = true;
36005         }
36006         this.collapsed = false;
36007         if(this.config.animate && skipAnim !== true){
36008             this.animateExpand();
36009         }else{
36010             this.el.show();
36011             if(this.split){
36012                 this.split.el.show();
36013             }
36014             this.collapsedEl.setLocation(-2000,-2000);
36015             this.collapsedEl.hide();
36016             this.fireEvent("invalidated", this);
36017             this.fireEvent("expanded", this);
36018         }
36019     },
36020 */
36021     animateExpand : function(){
36022         // overridden
36023     },
36024
36025     initTabs : function()
36026     {
36027         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36028         
36029         var ts = new Roo.bootstrap.panel.Tabs({
36030                 el: this.bodyEl.dom,
36031                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36032                 disableTooltips: this.config.disableTabTips,
36033                 toolbar : this.config.toolbar
36034             });
36035         
36036         if(this.config.hideTabs){
36037             ts.stripWrap.setDisplayed(false);
36038         }
36039         this.tabs = ts;
36040         ts.resizeTabs = this.config.resizeTabs === true;
36041         ts.minTabWidth = this.config.minTabWidth || 40;
36042         ts.maxTabWidth = this.config.maxTabWidth || 250;
36043         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36044         ts.monitorResize = false;
36045         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36046         ts.bodyEl.addClass('roo-layout-tabs-body');
36047         this.panels.each(this.initPanelAsTab, this);
36048     },
36049
36050     initPanelAsTab : function(panel){
36051         var ti = this.tabs.addTab(
36052             panel.getEl().id,
36053             panel.getTitle(),
36054             null,
36055             this.config.closeOnTab && panel.isClosable(),
36056             panel.tpl
36057         );
36058         if(panel.tabTip !== undefined){
36059             ti.setTooltip(panel.tabTip);
36060         }
36061         ti.on("activate", function(){
36062               this.setActivePanel(panel);
36063         }, this);
36064         
36065         if(this.config.closeOnTab){
36066             ti.on("beforeclose", function(t, e){
36067                 e.cancel = true;
36068                 this.remove(panel);
36069             }, this);
36070         }
36071         
36072         panel.tabItem = ti;
36073         
36074         return ti;
36075     },
36076
36077     updatePanelTitle : function(panel, title)
36078     {
36079         if(this.activePanel == panel){
36080             this.updateTitle(title);
36081         }
36082         if(this.tabs){
36083             var ti = this.tabs.getTab(panel.getEl().id);
36084             ti.setText(title);
36085             if(panel.tabTip !== undefined){
36086                 ti.setTooltip(panel.tabTip);
36087             }
36088         }
36089     },
36090
36091     updateTitle : function(title){
36092         if(this.titleTextEl && !this.config.title){
36093             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36094         }
36095     },
36096
36097     setActivePanel : function(panel)
36098     {
36099         panel = this.getPanel(panel);
36100         if(this.activePanel && this.activePanel != panel){
36101             if(this.activePanel.setActiveState(false) === false){
36102                 return;
36103             }
36104         }
36105         this.activePanel = panel;
36106         panel.setActiveState(true);
36107         if(this.panelSize){
36108             panel.setSize(this.panelSize.width, this.panelSize.height);
36109         }
36110         if(this.closeBtn){
36111             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36112         }
36113         this.updateTitle(panel.getTitle());
36114         if(this.tabs){
36115             this.fireEvent("invalidated", this);
36116         }
36117         this.fireEvent("panelactivated", this, panel);
36118     },
36119
36120     /**
36121      * Shows the specified panel.
36122      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36123      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36124      */
36125     showPanel : function(panel)
36126     {
36127         panel = this.getPanel(panel);
36128         if(panel){
36129             if(this.tabs){
36130                 var tab = this.tabs.getTab(panel.getEl().id);
36131                 if(tab.isHidden()){
36132                     this.tabs.unhideTab(tab.id);
36133                 }
36134                 tab.activate();
36135             }else{
36136                 this.setActivePanel(panel);
36137             }
36138         }
36139         return panel;
36140     },
36141
36142     /**
36143      * Get the active panel for this region.
36144      * @return {Roo.ContentPanel} The active panel or null
36145      */
36146     getActivePanel : function(){
36147         return this.activePanel;
36148     },
36149
36150     validateVisibility : function(){
36151         if(this.panels.getCount() < 1){
36152             this.updateTitle("&#160;");
36153             this.closeBtn.hide();
36154             this.hide();
36155         }else{
36156             if(!this.isVisible()){
36157                 this.show();
36158             }
36159         }
36160     },
36161
36162     /**
36163      * Adds the passed ContentPanel(s) to this region.
36164      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36165      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36166      */
36167     add : function(panel)
36168     {
36169         if(arguments.length > 1){
36170             for(var i = 0, len = arguments.length; i < len; i++) {
36171                 this.add(arguments[i]);
36172             }
36173             return null;
36174         }
36175         
36176         // if we have not been rendered yet, then we can not really do much of this..
36177         if (!this.bodyEl) {
36178             this.unrendered_panels.push(panel);
36179             return panel;
36180         }
36181         
36182         
36183         
36184         
36185         if(this.hasPanel(panel)){
36186             this.showPanel(panel);
36187             return panel;
36188         }
36189         panel.setRegion(this);
36190         this.panels.add(panel);
36191        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36192             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36193             // and hide them... ???
36194             this.bodyEl.dom.appendChild(panel.getEl().dom);
36195             if(panel.background !== true){
36196                 this.setActivePanel(panel);
36197             }
36198             this.fireEvent("paneladded", this, panel);
36199             return panel;
36200         }
36201         */
36202         if(!this.tabs){
36203             this.initTabs();
36204         }else{
36205             this.initPanelAsTab(panel);
36206         }
36207         
36208         
36209         if(panel.background !== true){
36210             this.tabs.activate(panel.getEl().id);
36211         }
36212         this.fireEvent("paneladded", this, panel);
36213         return panel;
36214     },
36215
36216     /**
36217      * Hides the tab for the specified panel.
36218      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36219      */
36220     hidePanel : function(panel){
36221         if(this.tabs && (panel = this.getPanel(panel))){
36222             this.tabs.hideTab(panel.getEl().id);
36223         }
36224     },
36225
36226     /**
36227      * Unhides the tab for a previously hidden panel.
36228      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36229      */
36230     unhidePanel : function(panel){
36231         if(this.tabs && (panel = this.getPanel(panel))){
36232             this.tabs.unhideTab(panel.getEl().id);
36233         }
36234     },
36235
36236     clearPanels : function(){
36237         while(this.panels.getCount() > 0){
36238              this.remove(this.panels.first());
36239         }
36240     },
36241
36242     /**
36243      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36244      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36245      * @param {Boolean} preservePanel Overrides the config preservePanel option
36246      * @return {Roo.ContentPanel} The panel that was removed
36247      */
36248     remove : function(panel, preservePanel)
36249     {
36250         panel = this.getPanel(panel);
36251         if(!panel){
36252             return null;
36253         }
36254         var e = {};
36255         this.fireEvent("beforeremove", this, panel, e);
36256         if(e.cancel === true){
36257             return null;
36258         }
36259         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36260         var panelId = panel.getId();
36261         this.panels.removeKey(panelId);
36262         if(preservePanel){
36263             document.body.appendChild(panel.getEl().dom);
36264         }
36265         if(this.tabs){
36266             this.tabs.removeTab(panel.getEl().id);
36267         }else if (!preservePanel){
36268             this.bodyEl.dom.removeChild(panel.getEl().dom);
36269         }
36270         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36271             var p = this.panels.first();
36272             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36273             tempEl.appendChild(p.getEl().dom);
36274             this.bodyEl.update("");
36275             this.bodyEl.dom.appendChild(p.getEl().dom);
36276             tempEl = null;
36277             this.updateTitle(p.getTitle());
36278             this.tabs = null;
36279             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36280             this.setActivePanel(p);
36281         }
36282         panel.setRegion(null);
36283         if(this.activePanel == panel){
36284             this.activePanel = null;
36285         }
36286         if(this.config.autoDestroy !== false && preservePanel !== true){
36287             try{panel.destroy();}catch(e){}
36288         }
36289         this.fireEvent("panelremoved", this, panel);
36290         return panel;
36291     },
36292
36293     /**
36294      * Returns the TabPanel component used by this region
36295      * @return {Roo.TabPanel}
36296      */
36297     getTabs : function(){
36298         return this.tabs;
36299     },
36300
36301     createTool : function(parentEl, className){
36302         var btn = Roo.DomHelper.append(parentEl, {
36303             tag: "div",
36304             cls: "x-layout-tools-button",
36305             children: [ {
36306                 tag: "div",
36307                 cls: "roo-layout-tools-button-inner " + className,
36308                 html: "&#160;"
36309             }]
36310         }, true);
36311         btn.addClassOnOver("roo-layout-tools-button-over");
36312         return btn;
36313     }
36314 });/*
36315  * Based on:
36316  * Ext JS Library 1.1.1
36317  * Copyright(c) 2006-2007, Ext JS, LLC.
36318  *
36319  * Originally Released Under LGPL - original licence link has changed is not relivant.
36320  *
36321  * Fork - LGPL
36322  * <script type="text/javascript">
36323  */
36324  
36325
36326
36327 /**
36328  * @class Roo.SplitLayoutRegion
36329  * @extends Roo.LayoutRegion
36330  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36331  */
36332 Roo.bootstrap.layout.Split = function(config){
36333     this.cursor = config.cursor;
36334     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36335 };
36336
36337 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36338 {
36339     splitTip : "Drag to resize.",
36340     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36341     useSplitTips : false,
36342
36343     applyConfig : function(config){
36344         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36345     },
36346     
36347     onRender : function(ctr,pos) {
36348         
36349         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36350         if(!this.config.split){
36351             return;
36352         }
36353         if(!this.split){
36354             
36355             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36356                             tag: "div",
36357                             id: this.el.id + "-split",
36358                             cls: "roo-layout-split roo-layout-split-"+this.position,
36359                             html: "&#160;"
36360             });
36361             /** The SplitBar for this region 
36362             * @type Roo.SplitBar */
36363             // does not exist yet...
36364             Roo.log([this.position, this.orientation]);
36365             
36366             this.split = new Roo.bootstrap.SplitBar({
36367                 dragElement : splitEl,
36368                 resizingElement: this.el,
36369                 orientation : this.orientation
36370             });
36371             
36372             this.split.on("moved", this.onSplitMove, this);
36373             this.split.useShim = this.config.useShim === true;
36374             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36375             if(this.useSplitTips){
36376                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36377             }
36378             //if(config.collapsible){
36379             //    this.split.el.on("dblclick", this.collapse,  this);
36380             //}
36381         }
36382         if(typeof this.config.minSize != "undefined"){
36383             this.split.minSize = this.config.minSize;
36384         }
36385         if(typeof this.config.maxSize != "undefined"){
36386             this.split.maxSize = this.config.maxSize;
36387         }
36388         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36389             this.hideSplitter();
36390         }
36391         
36392     },
36393
36394     getHMaxSize : function(){
36395          var cmax = this.config.maxSize || 10000;
36396          var center = this.mgr.getRegion("center");
36397          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36398     },
36399
36400     getVMaxSize : function(){
36401          var cmax = this.config.maxSize || 10000;
36402          var center = this.mgr.getRegion("center");
36403          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36404     },
36405
36406     onSplitMove : function(split, newSize){
36407         this.fireEvent("resized", this, newSize);
36408     },
36409     
36410     /** 
36411      * Returns the {@link Roo.SplitBar} for this region.
36412      * @return {Roo.SplitBar}
36413      */
36414     getSplitBar : function(){
36415         return this.split;
36416     },
36417     
36418     hide : function(){
36419         this.hideSplitter();
36420         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36421     },
36422
36423     hideSplitter : function(){
36424         if(this.split){
36425             this.split.el.setLocation(-2000,-2000);
36426             this.split.el.hide();
36427         }
36428     },
36429
36430     show : function(){
36431         if(this.split){
36432             this.split.el.show();
36433         }
36434         Roo.bootstrap.layout.Split.superclass.show.call(this);
36435     },
36436     
36437     beforeSlide: function(){
36438         if(Roo.isGecko){// firefox overflow auto bug workaround
36439             this.bodyEl.clip();
36440             if(this.tabs) {
36441                 this.tabs.bodyEl.clip();
36442             }
36443             if(this.activePanel){
36444                 this.activePanel.getEl().clip();
36445                 
36446                 if(this.activePanel.beforeSlide){
36447                     this.activePanel.beforeSlide();
36448                 }
36449             }
36450         }
36451     },
36452     
36453     afterSlide : function(){
36454         if(Roo.isGecko){// firefox overflow auto bug workaround
36455             this.bodyEl.unclip();
36456             if(this.tabs) {
36457                 this.tabs.bodyEl.unclip();
36458             }
36459             if(this.activePanel){
36460                 this.activePanel.getEl().unclip();
36461                 if(this.activePanel.afterSlide){
36462                     this.activePanel.afterSlide();
36463                 }
36464             }
36465         }
36466     },
36467
36468     initAutoHide : function(){
36469         if(this.autoHide !== false){
36470             if(!this.autoHideHd){
36471                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36472                 this.autoHideHd = {
36473                     "mouseout": function(e){
36474                         if(!e.within(this.el, true)){
36475                             st.delay(500);
36476                         }
36477                     },
36478                     "mouseover" : function(e){
36479                         st.cancel();
36480                     },
36481                     scope : this
36482                 };
36483             }
36484             this.el.on(this.autoHideHd);
36485         }
36486     },
36487
36488     clearAutoHide : function(){
36489         if(this.autoHide !== false){
36490             this.el.un("mouseout", this.autoHideHd.mouseout);
36491             this.el.un("mouseover", this.autoHideHd.mouseover);
36492         }
36493     },
36494
36495     clearMonitor : function(){
36496         Roo.get(document).un("click", this.slideInIf, this);
36497     },
36498
36499     // these names are backwards but not changed for compat
36500     slideOut : function(){
36501         if(this.isSlid || this.el.hasActiveFx()){
36502             return;
36503         }
36504         this.isSlid = true;
36505         if(this.collapseBtn){
36506             this.collapseBtn.hide();
36507         }
36508         this.closeBtnState = this.closeBtn.getStyle('display');
36509         this.closeBtn.hide();
36510         if(this.stickBtn){
36511             this.stickBtn.show();
36512         }
36513         this.el.show();
36514         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36515         this.beforeSlide();
36516         this.el.setStyle("z-index", 10001);
36517         this.el.slideIn(this.getSlideAnchor(), {
36518             callback: function(){
36519                 this.afterSlide();
36520                 this.initAutoHide();
36521                 Roo.get(document).on("click", this.slideInIf, this);
36522                 this.fireEvent("slideshow", this);
36523             },
36524             scope: this,
36525             block: true
36526         });
36527     },
36528
36529     afterSlideIn : function(){
36530         this.clearAutoHide();
36531         this.isSlid = false;
36532         this.clearMonitor();
36533         this.el.setStyle("z-index", "");
36534         if(this.collapseBtn){
36535             this.collapseBtn.show();
36536         }
36537         this.closeBtn.setStyle('display', this.closeBtnState);
36538         if(this.stickBtn){
36539             this.stickBtn.hide();
36540         }
36541         this.fireEvent("slidehide", this);
36542     },
36543
36544     slideIn : function(cb){
36545         if(!this.isSlid || this.el.hasActiveFx()){
36546             Roo.callback(cb);
36547             return;
36548         }
36549         this.isSlid = false;
36550         this.beforeSlide();
36551         this.el.slideOut(this.getSlideAnchor(), {
36552             callback: function(){
36553                 this.el.setLeftTop(-10000, -10000);
36554                 this.afterSlide();
36555                 this.afterSlideIn();
36556                 Roo.callback(cb);
36557             },
36558             scope: this,
36559             block: true
36560         });
36561     },
36562     
36563     slideInIf : function(e){
36564         if(!e.within(this.el)){
36565             this.slideIn();
36566         }
36567     },
36568
36569     animateCollapse : function(){
36570         this.beforeSlide();
36571         this.el.setStyle("z-index", 20000);
36572         var anchor = this.getSlideAnchor();
36573         this.el.slideOut(anchor, {
36574             callback : function(){
36575                 this.el.setStyle("z-index", "");
36576                 this.collapsedEl.slideIn(anchor, {duration:.3});
36577                 this.afterSlide();
36578                 this.el.setLocation(-10000,-10000);
36579                 this.el.hide();
36580                 this.fireEvent("collapsed", this);
36581             },
36582             scope: this,
36583             block: true
36584         });
36585     },
36586
36587     animateExpand : function(){
36588         this.beforeSlide();
36589         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36590         this.el.setStyle("z-index", 20000);
36591         this.collapsedEl.hide({
36592             duration:.1
36593         });
36594         this.el.slideIn(this.getSlideAnchor(), {
36595             callback : function(){
36596                 this.el.setStyle("z-index", "");
36597                 this.afterSlide();
36598                 if(this.split){
36599                     this.split.el.show();
36600                 }
36601                 this.fireEvent("invalidated", this);
36602                 this.fireEvent("expanded", this);
36603             },
36604             scope: this,
36605             block: true
36606         });
36607     },
36608
36609     anchors : {
36610         "west" : "left",
36611         "east" : "right",
36612         "north" : "top",
36613         "south" : "bottom"
36614     },
36615
36616     sanchors : {
36617         "west" : "l",
36618         "east" : "r",
36619         "north" : "t",
36620         "south" : "b"
36621     },
36622
36623     canchors : {
36624         "west" : "tl-tr",
36625         "east" : "tr-tl",
36626         "north" : "tl-bl",
36627         "south" : "bl-tl"
36628     },
36629
36630     getAnchor : function(){
36631         return this.anchors[this.position];
36632     },
36633
36634     getCollapseAnchor : function(){
36635         return this.canchors[this.position];
36636     },
36637
36638     getSlideAnchor : function(){
36639         return this.sanchors[this.position];
36640     },
36641
36642     getAlignAdj : function(){
36643         var cm = this.cmargins;
36644         switch(this.position){
36645             case "west":
36646                 return [0, 0];
36647             break;
36648             case "east":
36649                 return [0, 0];
36650             break;
36651             case "north":
36652                 return [0, 0];
36653             break;
36654             case "south":
36655                 return [0, 0];
36656             break;
36657         }
36658     },
36659
36660     getExpandAdj : function(){
36661         var c = this.collapsedEl, cm = this.cmargins;
36662         switch(this.position){
36663             case "west":
36664                 return [-(cm.right+c.getWidth()+cm.left), 0];
36665             break;
36666             case "east":
36667                 return [cm.right+c.getWidth()+cm.left, 0];
36668             break;
36669             case "north":
36670                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36671             break;
36672             case "south":
36673                 return [0, cm.top+cm.bottom+c.getHeight()];
36674             break;
36675         }
36676     }
36677 });/*
36678  * Based on:
36679  * Ext JS Library 1.1.1
36680  * Copyright(c) 2006-2007, Ext JS, LLC.
36681  *
36682  * Originally Released Under LGPL - original licence link has changed is not relivant.
36683  *
36684  * Fork - LGPL
36685  * <script type="text/javascript">
36686  */
36687 /*
36688  * These classes are private internal classes
36689  */
36690 Roo.bootstrap.layout.Center = function(config){
36691     config.region = "center";
36692     Roo.bootstrap.layout.Region.call(this, config);
36693     this.visible = true;
36694     this.minWidth = config.minWidth || 20;
36695     this.minHeight = config.minHeight || 20;
36696 };
36697
36698 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36699     hide : function(){
36700         // center panel can't be hidden
36701     },
36702     
36703     show : function(){
36704         // center panel can't be hidden
36705     },
36706     
36707     getMinWidth: function(){
36708         return this.minWidth;
36709     },
36710     
36711     getMinHeight: function(){
36712         return this.minHeight;
36713     }
36714 });
36715
36716
36717
36718
36719  
36720
36721
36722
36723
36724
36725 Roo.bootstrap.layout.North = function(config)
36726 {
36727     config.region = 'north';
36728     config.cursor = 'n-resize';
36729     
36730     Roo.bootstrap.layout.Split.call(this, config);
36731     
36732     
36733     if(this.split){
36734         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36735         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36736         this.split.el.addClass("roo-layout-split-v");
36737     }
36738     var size = config.initialSize || config.height;
36739     if(typeof size != "undefined"){
36740         this.el.setHeight(size);
36741     }
36742 };
36743 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36744 {
36745     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36746     
36747     
36748     
36749     getBox : function(){
36750         if(this.collapsed){
36751             return this.collapsedEl.getBox();
36752         }
36753         var box = this.el.getBox();
36754         if(this.split){
36755             box.height += this.split.el.getHeight();
36756         }
36757         return box;
36758     },
36759     
36760     updateBox : function(box){
36761         if(this.split && !this.collapsed){
36762             box.height -= this.split.el.getHeight();
36763             this.split.el.setLeft(box.x);
36764             this.split.el.setTop(box.y+box.height);
36765             this.split.el.setWidth(box.width);
36766         }
36767         if(this.collapsed){
36768             this.updateBody(box.width, null);
36769         }
36770         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36771     }
36772 });
36773
36774
36775
36776
36777
36778 Roo.bootstrap.layout.South = function(config){
36779     config.region = 'south';
36780     config.cursor = 's-resize';
36781     Roo.bootstrap.layout.Split.call(this, config);
36782     if(this.split){
36783         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36784         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36785         this.split.el.addClass("roo-layout-split-v");
36786     }
36787     var size = config.initialSize || config.height;
36788     if(typeof size != "undefined"){
36789         this.el.setHeight(size);
36790     }
36791 };
36792
36793 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36794     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36795     getBox : function(){
36796         if(this.collapsed){
36797             return this.collapsedEl.getBox();
36798         }
36799         var box = this.el.getBox();
36800         if(this.split){
36801             var sh = this.split.el.getHeight();
36802             box.height += sh;
36803             box.y -= sh;
36804         }
36805         return box;
36806     },
36807     
36808     updateBox : function(box){
36809         if(this.split && !this.collapsed){
36810             var sh = this.split.el.getHeight();
36811             box.height -= sh;
36812             box.y += sh;
36813             this.split.el.setLeft(box.x);
36814             this.split.el.setTop(box.y-sh);
36815             this.split.el.setWidth(box.width);
36816         }
36817         if(this.collapsed){
36818             this.updateBody(box.width, null);
36819         }
36820         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36821     }
36822 });
36823
36824 Roo.bootstrap.layout.East = function(config){
36825     config.region = "east";
36826     config.cursor = "e-resize";
36827     Roo.bootstrap.layout.Split.call(this, config);
36828     if(this.split){
36829         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36830         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36831         this.split.el.addClass("roo-layout-split-h");
36832     }
36833     var size = config.initialSize || config.width;
36834     if(typeof size != "undefined"){
36835         this.el.setWidth(size);
36836     }
36837 };
36838 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36839     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36840     getBox : function(){
36841         if(this.collapsed){
36842             return this.collapsedEl.getBox();
36843         }
36844         var box = this.el.getBox();
36845         if(this.split){
36846             var sw = this.split.el.getWidth();
36847             box.width += sw;
36848             box.x -= sw;
36849         }
36850         return box;
36851     },
36852
36853     updateBox : function(box){
36854         if(this.split && !this.collapsed){
36855             var sw = this.split.el.getWidth();
36856             box.width -= sw;
36857             this.split.el.setLeft(box.x);
36858             this.split.el.setTop(box.y);
36859             this.split.el.setHeight(box.height);
36860             box.x += sw;
36861         }
36862         if(this.collapsed){
36863             this.updateBody(null, box.height);
36864         }
36865         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36866     }
36867 });
36868
36869 Roo.bootstrap.layout.West = function(config){
36870     config.region = "west";
36871     config.cursor = "w-resize";
36872     
36873     Roo.bootstrap.layout.Split.call(this, config);
36874     if(this.split){
36875         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36876         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36877         this.split.el.addClass("roo-layout-split-h");
36878     }
36879     
36880 };
36881 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36882     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36883     
36884     onRender: function(ctr, pos)
36885     {
36886         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36887         var size = this.config.initialSize || this.config.width;
36888         if(typeof size != "undefined"){
36889             this.el.setWidth(size);
36890         }
36891     },
36892     
36893     getBox : function(){
36894         if(this.collapsed){
36895             return this.collapsedEl.getBox();
36896         }
36897         var box = this.el.getBox();
36898         if(this.split){
36899             box.width += this.split.el.getWidth();
36900         }
36901         return box;
36902     },
36903     
36904     updateBox : function(box){
36905         if(this.split && !this.collapsed){
36906             var sw = this.split.el.getWidth();
36907             box.width -= sw;
36908             this.split.el.setLeft(box.x+box.width);
36909             this.split.el.setTop(box.y);
36910             this.split.el.setHeight(box.height);
36911         }
36912         if(this.collapsed){
36913             this.updateBody(null, box.height);
36914         }
36915         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36916     }
36917 });
36918 Roo.namespace("Roo.bootstrap.panel");/*
36919  * Based on:
36920  * Ext JS Library 1.1.1
36921  * Copyright(c) 2006-2007, Ext JS, LLC.
36922  *
36923  * Originally Released Under LGPL - original licence link has changed is not relivant.
36924  *
36925  * Fork - LGPL
36926  * <script type="text/javascript">
36927  */
36928 /**
36929  * @class Roo.ContentPanel
36930  * @extends Roo.util.Observable
36931  * A basic ContentPanel element.
36932  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36933  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36934  * @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
36935  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36936  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36937  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36938  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36939  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36940  * @cfg {String} title          The title for this panel
36941  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36942  * @cfg {String} url            Calls {@link #setUrl} with this value
36943  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36944  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36945  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36946  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36947  * @cfg {Boolean} badges render the badges
36948
36949  * @constructor
36950  * Create a new ContentPanel.
36951  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36952  * @param {String/Object} config A string to set only the title or a config object
36953  * @param {String} content (optional) Set the HTML content for this panel
36954  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36955  */
36956 Roo.bootstrap.panel.Content = function( config){
36957     
36958     this.tpl = config.tpl || false;
36959     
36960     var el = config.el;
36961     var content = config.content;
36962
36963     if(config.autoCreate){ // xtype is available if this is called from factory
36964         el = Roo.id();
36965     }
36966     this.el = Roo.get(el);
36967     if(!this.el && config && config.autoCreate){
36968         if(typeof config.autoCreate == "object"){
36969             if(!config.autoCreate.id){
36970                 config.autoCreate.id = config.id||el;
36971             }
36972             this.el = Roo.DomHelper.append(document.body,
36973                         config.autoCreate, true);
36974         }else{
36975             var elcfg =  {   tag: "div",
36976                             cls: "roo-layout-inactive-content",
36977                             id: config.id||el
36978                             };
36979             if (config.html) {
36980                 elcfg.html = config.html;
36981                 
36982             }
36983                         
36984             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36985         }
36986     } 
36987     this.closable = false;
36988     this.loaded = false;
36989     this.active = false;
36990    
36991       
36992     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36993         
36994         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36995         
36996         this.wrapEl = this.el; //this.el.wrap();
36997         var ti = [];
36998         if (config.toolbar.items) {
36999             ti = config.toolbar.items ;
37000             delete config.toolbar.items ;
37001         }
37002         
37003         var nitems = [];
37004         this.toolbar.render(this.wrapEl, 'before');
37005         for(var i =0;i < ti.length;i++) {
37006           //  Roo.log(['add child', items[i]]);
37007             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37008         }
37009         this.toolbar.items = nitems;
37010         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37011         delete config.toolbar;
37012         
37013     }
37014     /*
37015     // xtype created footer. - not sure if will work as we normally have to render first..
37016     if (this.footer && !this.footer.el && this.footer.xtype) {
37017         if (!this.wrapEl) {
37018             this.wrapEl = this.el.wrap();
37019         }
37020     
37021         this.footer.container = this.wrapEl.createChild();
37022          
37023         this.footer = Roo.factory(this.footer, Roo);
37024         
37025     }
37026     */
37027     
37028      if(typeof config == "string"){
37029         this.title = config;
37030     }else{
37031         Roo.apply(this, config);
37032     }
37033     
37034     if(this.resizeEl){
37035         this.resizeEl = Roo.get(this.resizeEl, true);
37036     }else{
37037         this.resizeEl = this.el;
37038     }
37039     // handle view.xtype
37040     
37041  
37042     
37043     
37044     this.addEvents({
37045         /**
37046          * @event activate
37047          * Fires when this panel is activated. 
37048          * @param {Roo.ContentPanel} this
37049          */
37050         "activate" : true,
37051         /**
37052          * @event deactivate
37053          * Fires when this panel is activated. 
37054          * @param {Roo.ContentPanel} this
37055          */
37056         "deactivate" : true,
37057
37058         /**
37059          * @event resize
37060          * Fires when this panel is resized if fitToFrame is true.
37061          * @param {Roo.ContentPanel} this
37062          * @param {Number} width The width after any component adjustments
37063          * @param {Number} height The height after any component adjustments
37064          */
37065         "resize" : true,
37066         
37067          /**
37068          * @event render
37069          * Fires when this tab is created
37070          * @param {Roo.ContentPanel} this
37071          */
37072         "render" : true
37073         
37074         
37075         
37076     });
37077     
37078
37079     
37080     
37081     if(this.autoScroll){
37082         this.resizeEl.setStyle("overflow", "auto");
37083     } else {
37084         // fix randome scrolling
37085         //this.el.on('scroll', function() {
37086         //    Roo.log('fix random scolling');
37087         //    this.scrollTo('top',0); 
37088         //});
37089     }
37090     content = content || this.content;
37091     if(content){
37092         this.setContent(content);
37093     }
37094     if(config && config.url){
37095         this.setUrl(this.url, this.params, this.loadOnce);
37096     }
37097     
37098     
37099     
37100     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37101     
37102     if (this.view && typeof(this.view.xtype) != 'undefined') {
37103         this.view.el = this.el.appendChild(document.createElement("div"));
37104         this.view = Roo.factory(this.view); 
37105         this.view.render  &&  this.view.render(false, '');  
37106     }
37107     
37108     
37109     this.fireEvent('render', this);
37110 };
37111
37112 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37113     
37114     tabTip : '',
37115     
37116     setRegion : function(region){
37117         this.region = region;
37118         this.setActiveClass(region && !this.background);
37119     },
37120     
37121     
37122     setActiveClass: function(state)
37123     {
37124         if(state){
37125            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37126            this.el.setStyle('position','relative');
37127         }else{
37128            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37129            this.el.setStyle('position', 'absolute');
37130         } 
37131     },
37132     
37133     /**
37134      * Returns the toolbar for this Panel if one was configured. 
37135      * @return {Roo.Toolbar} 
37136      */
37137     getToolbar : function(){
37138         return this.toolbar;
37139     },
37140     
37141     setActiveState : function(active)
37142     {
37143         this.active = active;
37144         this.setActiveClass(active);
37145         if(!active){
37146             if(this.fireEvent("deactivate", this) === false){
37147                 return false;
37148             }
37149             return true;
37150         }
37151         this.fireEvent("activate", this);
37152         return true;
37153     },
37154     /**
37155      * Updates this panel's element
37156      * @param {String} content The new content
37157      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37158     */
37159     setContent : function(content, loadScripts){
37160         this.el.update(content, loadScripts);
37161     },
37162
37163     ignoreResize : function(w, h){
37164         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37165             return true;
37166         }else{
37167             this.lastSize = {width: w, height: h};
37168             return false;
37169         }
37170     },
37171     /**
37172      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37173      * @return {Roo.UpdateManager} The UpdateManager
37174      */
37175     getUpdateManager : function(){
37176         return this.el.getUpdateManager();
37177     },
37178      /**
37179      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37180      * @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:
37181 <pre><code>
37182 panel.load({
37183     url: "your-url.php",
37184     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37185     callback: yourFunction,
37186     scope: yourObject, //(optional scope)
37187     discardUrl: false,
37188     nocache: false,
37189     text: "Loading...",
37190     timeout: 30,
37191     scripts: false
37192 });
37193 </code></pre>
37194      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37195      * 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.
37196      * @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}
37197      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37198      * @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.
37199      * @return {Roo.ContentPanel} this
37200      */
37201     load : function(){
37202         var um = this.el.getUpdateManager();
37203         um.update.apply(um, arguments);
37204         return this;
37205     },
37206
37207
37208     /**
37209      * 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.
37210      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37211      * @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)
37212      * @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)
37213      * @return {Roo.UpdateManager} The UpdateManager
37214      */
37215     setUrl : function(url, params, loadOnce){
37216         if(this.refreshDelegate){
37217             this.removeListener("activate", this.refreshDelegate);
37218         }
37219         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37220         this.on("activate", this.refreshDelegate);
37221         return this.el.getUpdateManager();
37222     },
37223     
37224     _handleRefresh : function(url, params, loadOnce){
37225         if(!loadOnce || !this.loaded){
37226             var updater = this.el.getUpdateManager();
37227             updater.update(url, params, this._setLoaded.createDelegate(this));
37228         }
37229     },
37230     
37231     _setLoaded : function(){
37232         this.loaded = true;
37233     }, 
37234     
37235     /**
37236      * Returns this panel's id
37237      * @return {String} 
37238      */
37239     getId : function(){
37240         return this.el.id;
37241     },
37242     
37243     /** 
37244      * Returns this panel's element - used by regiosn to add.
37245      * @return {Roo.Element} 
37246      */
37247     getEl : function(){
37248         return this.wrapEl || this.el;
37249     },
37250     
37251    
37252     
37253     adjustForComponents : function(width, height)
37254     {
37255         //Roo.log('adjustForComponents ');
37256         if(this.resizeEl != this.el){
37257             width -= this.el.getFrameWidth('lr');
37258             height -= this.el.getFrameWidth('tb');
37259         }
37260         if(this.toolbar){
37261             var te = this.toolbar.getEl();
37262             te.setWidth(width);
37263             height -= te.getHeight();
37264         }
37265         if(this.footer){
37266             var te = this.footer.getEl();
37267             te.setWidth(width);
37268             height -= te.getHeight();
37269         }
37270         
37271         
37272         if(this.adjustments){
37273             width += this.adjustments[0];
37274             height += this.adjustments[1];
37275         }
37276         return {"width": width, "height": height};
37277     },
37278     
37279     setSize : function(width, height){
37280         if(this.fitToFrame && !this.ignoreResize(width, height)){
37281             if(this.fitContainer && this.resizeEl != this.el){
37282                 this.el.setSize(width, height);
37283             }
37284             var size = this.adjustForComponents(width, height);
37285             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37286             this.fireEvent('resize', this, size.width, size.height);
37287         }
37288     },
37289     
37290     /**
37291      * Returns this panel's title
37292      * @return {String} 
37293      */
37294     getTitle : function(){
37295         
37296         if (typeof(this.title) != 'object') {
37297             return this.title;
37298         }
37299         
37300         var t = '';
37301         for (var k in this.title) {
37302             if (!this.title.hasOwnProperty(k)) {
37303                 continue;
37304             }
37305             
37306             if (k.indexOf('-') >= 0) {
37307                 var s = k.split('-');
37308                 for (var i = 0; i<s.length; i++) {
37309                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37310                 }
37311             } else {
37312                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37313             }
37314         }
37315         return t;
37316     },
37317     
37318     /**
37319      * Set this panel's title
37320      * @param {String} title
37321      */
37322     setTitle : function(title){
37323         this.title = title;
37324         if(this.region){
37325             this.region.updatePanelTitle(this, title);
37326         }
37327     },
37328     
37329     /**
37330      * Returns true is this panel was configured to be closable
37331      * @return {Boolean} 
37332      */
37333     isClosable : function(){
37334         return this.closable;
37335     },
37336     
37337     beforeSlide : function(){
37338         this.el.clip();
37339         this.resizeEl.clip();
37340     },
37341     
37342     afterSlide : function(){
37343         this.el.unclip();
37344         this.resizeEl.unclip();
37345     },
37346     
37347     /**
37348      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37349      *   Will fail silently if the {@link #setUrl} method has not been called.
37350      *   This does not activate the panel, just updates its content.
37351      */
37352     refresh : function(){
37353         if(this.refreshDelegate){
37354            this.loaded = false;
37355            this.refreshDelegate();
37356         }
37357     },
37358     
37359     /**
37360      * Destroys this panel
37361      */
37362     destroy : function(){
37363         this.el.removeAllListeners();
37364         var tempEl = document.createElement("span");
37365         tempEl.appendChild(this.el.dom);
37366         tempEl.innerHTML = "";
37367         this.el.remove();
37368         this.el = null;
37369     },
37370     
37371     /**
37372      * form - if the content panel contains a form - this is a reference to it.
37373      * @type {Roo.form.Form}
37374      */
37375     form : false,
37376     /**
37377      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37378      *    This contains a reference to it.
37379      * @type {Roo.View}
37380      */
37381     view : false,
37382     
37383       /**
37384      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37385      * <pre><code>
37386
37387 layout.addxtype({
37388        xtype : 'Form',
37389        items: [ .... ]
37390    }
37391 );
37392
37393 </code></pre>
37394      * @param {Object} cfg Xtype definition of item to add.
37395      */
37396     
37397     
37398     getChildContainer: function () {
37399         return this.getEl();
37400     }
37401     
37402     
37403     /*
37404         var  ret = new Roo.factory(cfg);
37405         return ret;
37406         
37407         
37408         // add form..
37409         if (cfg.xtype.match(/^Form$/)) {
37410             
37411             var el;
37412             //if (this.footer) {
37413             //    el = this.footer.container.insertSibling(false, 'before');
37414             //} else {
37415                 el = this.el.createChild();
37416             //}
37417
37418             this.form = new  Roo.form.Form(cfg);
37419             
37420             
37421             if ( this.form.allItems.length) {
37422                 this.form.render(el.dom);
37423             }
37424             return this.form;
37425         }
37426         // should only have one of theses..
37427         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37428             // views.. should not be just added - used named prop 'view''
37429             
37430             cfg.el = this.el.appendChild(document.createElement("div"));
37431             // factory?
37432             
37433             var ret = new Roo.factory(cfg);
37434              
37435              ret.render && ret.render(false, ''); // render blank..
37436             this.view = ret;
37437             return ret;
37438         }
37439         return false;
37440     }
37441     \*/
37442 });
37443  
37444 /**
37445  * @class Roo.bootstrap.panel.Grid
37446  * @extends Roo.bootstrap.panel.Content
37447  * @constructor
37448  * Create a new GridPanel.
37449  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37450  * @param {Object} config A the config object
37451   
37452  */
37453
37454
37455
37456 Roo.bootstrap.panel.Grid = function(config)
37457 {
37458     
37459       
37460     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37461         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37462
37463     config.el = this.wrapper;
37464     //this.el = this.wrapper;
37465     
37466       if (config.container) {
37467         // ctor'ed from a Border/panel.grid
37468         
37469         
37470         this.wrapper.setStyle("overflow", "hidden");
37471         this.wrapper.addClass('roo-grid-container');
37472
37473     }
37474     
37475     
37476     if(config.toolbar){
37477         var tool_el = this.wrapper.createChild();    
37478         this.toolbar = Roo.factory(config.toolbar);
37479         var ti = [];
37480         if (config.toolbar.items) {
37481             ti = config.toolbar.items ;
37482             delete config.toolbar.items ;
37483         }
37484         
37485         var nitems = [];
37486         this.toolbar.render(tool_el);
37487         for(var i =0;i < ti.length;i++) {
37488           //  Roo.log(['add child', items[i]]);
37489             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37490         }
37491         this.toolbar.items = nitems;
37492         
37493         delete config.toolbar;
37494     }
37495     
37496     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37497     config.grid.scrollBody = true;;
37498     config.grid.monitorWindowResize = false; // turn off autosizing
37499     config.grid.autoHeight = false;
37500     config.grid.autoWidth = false;
37501     
37502     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37503     
37504     if (config.background) {
37505         // render grid on panel activation (if panel background)
37506         this.on('activate', function(gp) {
37507             if (!gp.grid.rendered) {
37508                 gp.grid.render(this.wrapper);
37509                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37510             }
37511         });
37512             
37513     } else {
37514         this.grid.render(this.wrapper);
37515         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37516
37517     }
37518     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37519     // ??? needed ??? config.el = this.wrapper;
37520     
37521     
37522     
37523   
37524     // xtype created footer. - not sure if will work as we normally have to render first..
37525     if (this.footer && !this.footer.el && this.footer.xtype) {
37526         
37527         var ctr = this.grid.getView().getFooterPanel(true);
37528         this.footer.dataSource = this.grid.dataSource;
37529         this.footer = Roo.factory(this.footer, Roo);
37530         this.footer.render(ctr);
37531         
37532     }
37533     
37534     
37535     
37536     
37537      
37538 };
37539
37540 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37541     getId : function(){
37542         return this.grid.id;
37543     },
37544     
37545     /**
37546      * Returns the grid for this panel
37547      * @return {Roo.bootstrap.Table} 
37548      */
37549     getGrid : function(){
37550         return this.grid;    
37551     },
37552     
37553     setSize : function(width, height){
37554         if(!this.ignoreResize(width, height)){
37555             var grid = this.grid;
37556             var size = this.adjustForComponents(width, height);
37557             var gridel = grid.getGridEl();
37558             gridel.setSize(size.width, size.height);
37559             /*
37560             var thd = grid.getGridEl().select('thead',true).first();
37561             var tbd = grid.getGridEl().select('tbody', true).first();
37562             if (tbd) {
37563                 tbd.setSize(width, height - thd.getHeight());
37564             }
37565             */
37566             grid.autoSize();
37567         }
37568     },
37569      
37570     
37571     
37572     beforeSlide : function(){
37573         this.grid.getView().scroller.clip();
37574     },
37575     
37576     afterSlide : function(){
37577         this.grid.getView().scroller.unclip();
37578     },
37579     
37580     destroy : function(){
37581         this.grid.destroy();
37582         delete this.grid;
37583         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37584     }
37585 });
37586
37587 /**
37588  * @class Roo.bootstrap.panel.Nest
37589  * @extends Roo.bootstrap.panel.Content
37590  * @constructor
37591  * Create a new Panel, that can contain a layout.Border.
37592  * 
37593  * 
37594  * @param {Roo.BorderLayout} layout The layout for this panel
37595  * @param {String/Object} config A string to set only the title or a config object
37596  */
37597 Roo.bootstrap.panel.Nest = function(config)
37598 {
37599     // construct with only one argument..
37600     /* FIXME - implement nicer consturctors
37601     if (layout.layout) {
37602         config = layout;
37603         layout = config.layout;
37604         delete config.layout;
37605     }
37606     if (layout.xtype && !layout.getEl) {
37607         // then layout needs constructing..
37608         layout = Roo.factory(layout, Roo);
37609     }
37610     */
37611     
37612     config.el =  config.layout.getEl();
37613     
37614     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37615     
37616     config.layout.monitorWindowResize = false; // turn off autosizing
37617     this.layout = config.layout;
37618     this.layout.getEl().addClass("roo-layout-nested-layout");
37619     
37620     
37621     
37622     
37623 };
37624
37625 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37626
37627     setSize : function(width, height){
37628         if(!this.ignoreResize(width, height)){
37629             var size = this.adjustForComponents(width, height);
37630             var el = this.layout.getEl();
37631             if (size.height < 1) {
37632                 el.setWidth(size.width);   
37633             } else {
37634                 el.setSize(size.width, size.height);
37635             }
37636             var touch = el.dom.offsetWidth;
37637             this.layout.layout();
37638             // ie requires a double layout on the first pass
37639             if(Roo.isIE && !this.initialized){
37640                 this.initialized = true;
37641                 this.layout.layout();
37642             }
37643         }
37644     },
37645     
37646     // activate all subpanels if not currently active..
37647     
37648     setActiveState : function(active){
37649         this.active = active;
37650         this.setActiveClass(active);
37651         
37652         if(!active){
37653             this.fireEvent("deactivate", this);
37654             return;
37655         }
37656         
37657         this.fireEvent("activate", this);
37658         // not sure if this should happen before or after..
37659         if (!this.layout) {
37660             return; // should not happen..
37661         }
37662         var reg = false;
37663         for (var r in this.layout.regions) {
37664             reg = this.layout.getRegion(r);
37665             if (reg.getActivePanel()) {
37666                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37667                 reg.setActivePanel(reg.getActivePanel());
37668                 continue;
37669             }
37670             if (!reg.panels.length) {
37671                 continue;
37672             }
37673             reg.showPanel(reg.getPanel(0));
37674         }
37675         
37676         
37677         
37678         
37679     },
37680     
37681     /**
37682      * Returns the nested BorderLayout for this panel
37683      * @return {Roo.BorderLayout} 
37684      */
37685     getLayout : function(){
37686         return this.layout;
37687     },
37688     
37689      /**
37690      * Adds a xtype elements to the layout of the nested panel
37691      * <pre><code>
37692
37693 panel.addxtype({
37694        xtype : 'ContentPanel',
37695        region: 'west',
37696        items: [ .... ]
37697    }
37698 );
37699
37700 panel.addxtype({
37701         xtype : 'NestedLayoutPanel',
37702         region: 'west',
37703         layout: {
37704            center: { },
37705            west: { }   
37706         },
37707         items : [ ... list of content panels or nested layout panels.. ]
37708    }
37709 );
37710 </code></pre>
37711      * @param {Object} cfg Xtype definition of item to add.
37712      */
37713     addxtype : function(cfg) {
37714         return this.layout.addxtype(cfg);
37715     
37716     }
37717 });        /*
37718  * Based on:
37719  * Ext JS Library 1.1.1
37720  * Copyright(c) 2006-2007, Ext JS, LLC.
37721  *
37722  * Originally Released Under LGPL - original licence link has changed is not relivant.
37723  *
37724  * Fork - LGPL
37725  * <script type="text/javascript">
37726  */
37727 /**
37728  * @class Roo.TabPanel
37729  * @extends Roo.util.Observable
37730  * A lightweight tab container.
37731  * <br><br>
37732  * Usage:
37733  * <pre><code>
37734 // basic tabs 1, built from existing content
37735 var tabs = new Roo.TabPanel("tabs1");
37736 tabs.addTab("script", "View Script");
37737 tabs.addTab("markup", "View Markup");
37738 tabs.activate("script");
37739
37740 // more advanced tabs, built from javascript
37741 var jtabs = new Roo.TabPanel("jtabs");
37742 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37743
37744 // set up the UpdateManager
37745 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37746 var updater = tab2.getUpdateManager();
37747 updater.setDefaultUrl("ajax1.htm");
37748 tab2.on('activate', updater.refresh, updater, true);
37749
37750 // Use setUrl for Ajax loading
37751 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37752 tab3.setUrl("ajax2.htm", null, true);
37753
37754 // Disabled tab
37755 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37756 tab4.disable();
37757
37758 jtabs.activate("jtabs-1");
37759  * </code></pre>
37760  * @constructor
37761  * Create a new TabPanel.
37762  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37763  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37764  */
37765 Roo.bootstrap.panel.Tabs = function(config){
37766     /**
37767     * The container element for this TabPanel.
37768     * @type Roo.Element
37769     */
37770     this.el = Roo.get(config.el);
37771     delete config.el;
37772     if(config){
37773         if(typeof config == "boolean"){
37774             this.tabPosition = config ? "bottom" : "top";
37775         }else{
37776             Roo.apply(this, config);
37777         }
37778     }
37779     
37780     if(this.tabPosition == "bottom"){
37781         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37782         this.el.addClass("roo-tabs-bottom");
37783     }
37784     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37785     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37786     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37787     if(Roo.isIE){
37788         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37789     }
37790     if(this.tabPosition != "bottom"){
37791         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37792          * @type Roo.Element
37793          */
37794         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37795         this.el.addClass("roo-tabs-top");
37796     }
37797     this.items = [];
37798
37799     this.bodyEl.setStyle("position", "relative");
37800
37801     this.active = null;
37802     this.activateDelegate = this.activate.createDelegate(this);
37803
37804     this.addEvents({
37805         /**
37806          * @event tabchange
37807          * Fires when the active tab changes
37808          * @param {Roo.TabPanel} this
37809          * @param {Roo.TabPanelItem} activePanel The new active tab
37810          */
37811         "tabchange": true,
37812         /**
37813          * @event beforetabchange
37814          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37815          * @param {Roo.TabPanel} this
37816          * @param {Object} e Set cancel to true on this object to cancel the tab change
37817          * @param {Roo.TabPanelItem} tab The tab being changed to
37818          */
37819         "beforetabchange" : true
37820     });
37821
37822     Roo.EventManager.onWindowResize(this.onResize, this);
37823     this.cpad = this.el.getPadding("lr");
37824     this.hiddenCount = 0;
37825
37826
37827     // toolbar on the tabbar support...
37828     if (this.toolbar) {
37829         alert("no toolbar support yet");
37830         this.toolbar  = false;
37831         /*
37832         var tcfg = this.toolbar;
37833         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37834         this.toolbar = new Roo.Toolbar(tcfg);
37835         if (Roo.isSafari) {
37836             var tbl = tcfg.container.child('table', true);
37837             tbl.setAttribute('width', '100%');
37838         }
37839         */
37840         
37841     }
37842    
37843
37844
37845     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37846 };
37847
37848 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37849     /*
37850      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37851      */
37852     tabPosition : "top",
37853     /*
37854      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37855      */
37856     currentTabWidth : 0,
37857     /*
37858      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37859      */
37860     minTabWidth : 40,
37861     /*
37862      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37863      */
37864     maxTabWidth : 250,
37865     /*
37866      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37867      */
37868     preferredTabWidth : 175,
37869     /*
37870      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37871      */
37872     resizeTabs : false,
37873     /*
37874      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37875      */
37876     monitorResize : true,
37877     /*
37878      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37879      */
37880     toolbar : false,
37881
37882     /**
37883      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37884      * @param {String} id The id of the div to use <b>or create</b>
37885      * @param {String} text The text for the tab
37886      * @param {String} content (optional) Content to put in the TabPanelItem body
37887      * @param {Boolean} closable (optional) True to create a close icon on the tab
37888      * @return {Roo.TabPanelItem} The created TabPanelItem
37889      */
37890     addTab : function(id, text, content, closable, tpl)
37891     {
37892         var item = new Roo.bootstrap.panel.TabItem({
37893             panel: this,
37894             id : id,
37895             text : text,
37896             closable : closable,
37897             tpl : tpl
37898         });
37899         this.addTabItem(item);
37900         if(content){
37901             item.setContent(content);
37902         }
37903         return item;
37904     },
37905
37906     /**
37907      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37908      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37909      * @return {Roo.TabPanelItem}
37910      */
37911     getTab : function(id){
37912         return this.items[id];
37913     },
37914
37915     /**
37916      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37917      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37918      */
37919     hideTab : function(id){
37920         var t = this.items[id];
37921         if(!t.isHidden()){
37922            t.setHidden(true);
37923            this.hiddenCount++;
37924            this.autoSizeTabs();
37925         }
37926     },
37927
37928     /**
37929      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37930      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37931      */
37932     unhideTab : function(id){
37933         var t = this.items[id];
37934         if(t.isHidden()){
37935            t.setHidden(false);
37936            this.hiddenCount--;
37937            this.autoSizeTabs();
37938         }
37939     },
37940
37941     /**
37942      * Adds an existing {@link Roo.TabPanelItem}.
37943      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37944      */
37945     addTabItem : function(item){
37946         this.items[item.id] = item;
37947         this.items.push(item);
37948       //  if(this.resizeTabs){
37949     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37950   //         this.autoSizeTabs();
37951 //        }else{
37952 //            item.autoSize();
37953        // }
37954     },
37955
37956     /**
37957      * Removes a {@link Roo.TabPanelItem}.
37958      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37959      */
37960     removeTab : function(id){
37961         var items = this.items;
37962         var tab = items[id];
37963         if(!tab) { return; }
37964         var index = items.indexOf(tab);
37965         if(this.active == tab && items.length > 1){
37966             var newTab = this.getNextAvailable(index);
37967             if(newTab) {
37968                 newTab.activate();
37969             }
37970         }
37971         this.stripEl.dom.removeChild(tab.pnode.dom);
37972         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37973             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37974         }
37975         items.splice(index, 1);
37976         delete this.items[tab.id];
37977         tab.fireEvent("close", tab);
37978         tab.purgeListeners();
37979         this.autoSizeTabs();
37980     },
37981
37982     getNextAvailable : function(start){
37983         var items = this.items;
37984         var index = start;
37985         // look for a next tab that will slide over to
37986         // replace the one being removed
37987         while(index < items.length){
37988             var item = items[++index];
37989             if(item && !item.isHidden()){
37990                 return item;
37991             }
37992         }
37993         // if one isn't found select the previous tab (on the left)
37994         index = start;
37995         while(index >= 0){
37996             var item = items[--index];
37997             if(item && !item.isHidden()){
37998                 return item;
37999             }
38000         }
38001         return null;
38002     },
38003
38004     /**
38005      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38006      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38007      */
38008     disableTab : function(id){
38009         var tab = this.items[id];
38010         if(tab && this.active != tab){
38011             tab.disable();
38012         }
38013     },
38014
38015     /**
38016      * Enables a {@link Roo.TabPanelItem} that is disabled.
38017      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38018      */
38019     enableTab : function(id){
38020         var tab = this.items[id];
38021         tab.enable();
38022     },
38023
38024     /**
38025      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38026      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38027      * @return {Roo.TabPanelItem} The TabPanelItem.
38028      */
38029     activate : function(id){
38030         var tab = this.items[id];
38031         if(!tab){
38032             return null;
38033         }
38034         if(tab == this.active || tab.disabled){
38035             return tab;
38036         }
38037         var e = {};
38038         this.fireEvent("beforetabchange", this, e, tab);
38039         if(e.cancel !== true && !tab.disabled){
38040             if(this.active){
38041                 this.active.hide();
38042             }
38043             this.active = this.items[id];
38044             this.active.show();
38045             this.fireEvent("tabchange", this, this.active);
38046         }
38047         return tab;
38048     },
38049
38050     /**
38051      * Gets the active {@link Roo.TabPanelItem}.
38052      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38053      */
38054     getActiveTab : function(){
38055         return this.active;
38056     },
38057
38058     /**
38059      * Updates the tab body element to fit the height of the container element
38060      * for overflow scrolling
38061      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38062      */
38063     syncHeight : function(targetHeight){
38064         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38065         var bm = this.bodyEl.getMargins();
38066         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38067         this.bodyEl.setHeight(newHeight);
38068         return newHeight;
38069     },
38070
38071     onResize : function(){
38072         if(this.monitorResize){
38073             this.autoSizeTabs();
38074         }
38075     },
38076
38077     /**
38078      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38079      */
38080     beginUpdate : function(){
38081         this.updating = true;
38082     },
38083
38084     /**
38085      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38086      */
38087     endUpdate : function(){
38088         this.updating = false;
38089         this.autoSizeTabs();
38090     },
38091
38092     /**
38093      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38094      */
38095     autoSizeTabs : function(){
38096         var count = this.items.length;
38097         var vcount = count - this.hiddenCount;
38098         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38099             return;
38100         }
38101         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38102         var availWidth = Math.floor(w / vcount);
38103         var b = this.stripBody;
38104         if(b.getWidth() > w){
38105             var tabs = this.items;
38106             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38107             if(availWidth < this.minTabWidth){
38108                 /*if(!this.sleft){    // incomplete scrolling code
38109                     this.createScrollButtons();
38110                 }
38111                 this.showScroll();
38112                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38113             }
38114         }else{
38115             if(this.currentTabWidth < this.preferredTabWidth){
38116                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38117             }
38118         }
38119     },
38120
38121     /**
38122      * Returns the number of tabs in this TabPanel.
38123      * @return {Number}
38124      */
38125      getCount : function(){
38126          return this.items.length;
38127      },
38128
38129     /**
38130      * Resizes all the tabs to the passed width
38131      * @param {Number} The new width
38132      */
38133     setTabWidth : function(width){
38134         this.currentTabWidth = width;
38135         for(var i = 0, len = this.items.length; i < len; i++) {
38136                 if(!this.items[i].isHidden()) {
38137                 this.items[i].setWidth(width);
38138             }
38139         }
38140     },
38141
38142     /**
38143      * Destroys this TabPanel
38144      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38145      */
38146     destroy : function(removeEl){
38147         Roo.EventManager.removeResizeListener(this.onResize, this);
38148         for(var i = 0, len = this.items.length; i < len; i++){
38149             this.items[i].purgeListeners();
38150         }
38151         if(removeEl === true){
38152             this.el.update("");
38153             this.el.remove();
38154         }
38155     },
38156     
38157     createStrip : function(container)
38158     {
38159         var strip = document.createElement("nav");
38160         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38161         container.appendChild(strip);
38162         return strip;
38163     },
38164     
38165     createStripList : function(strip)
38166     {
38167         // div wrapper for retard IE
38168         // returns the "tr" element.
38169         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38170         //'<div class="x-tabs-strip-wrap">'+
38171           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38172           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38173         return strip.firstChild; //.firstChild.firstChild.firstChild;
38174     },
38175     createBody : function(container)
38176     {
38177         var body = document.createElement("div");
38178         Roo.id(body, "tab-body");
38179         //Roo.fly(body).addClass("x-tabs-body");
38180         Roo.fly(body).addClass("tab-content");
38181         container.appendChild(body);
38182         return body;
38183     },
38184     createItemBody :function(bodyEl, id){
38185         var body = Roo.getDom(id);
38186         if(!body){
38187             body = document.createElement("div");
38188             body.id = id;
38189         }
38190         //Roo.fly(body).addClass("x-tabs-item-body");
38191         Roo.fly(body).addClass("tab-pane");
38192          bodyEl.insertBefore(body, bodyEl.firstChild);
38193         return body;
38194     },
38195     /** @private */
38196     createStripElements :  function(stripEl, text, closable, tpl)
38197     {
38198         var td = document.createElement("li"); // was td..
38199         
38200         
38201         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38202         
38203         
38204         stripEl.appendChild(td);
38205         /*if(closable){
38206             td.className = "x-tabs-closable";
38207             if(!this.closeTpl){
38208                 this.closeTpl = new Roo.Template(
38209                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38210                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38211                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38212                 );
38213             }
38214             var el = this.closeTpl.overwrite(td, {"text": text});
38215             var close = el.getElementsByTagName("div")[0];
38216             var inner = el.getElementsByTagName("em")[0];
38217             return {"el": el, "close": close, "inner": inner};
38218         } else {
38219         */
38220         // not sure what this is..
38221 //            if(!this.tabTpl){
38222                 //this.tabTpl = new Roo.Template(
38223                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38224                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38225                 //);
38226 //                this.tabTpl = new Roo.Template(
38227 //                   '<a href="#">' +
38228 //                   '<span unselectable="on"' +
38229 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38230 //                            ' >{text}</span></a>'
38231 //                );
38232 //                
38233 //            }
38234
38235
38236             var template = tpl || this.tabTpl || false;
38237             
38238             if(!template){
38239                 
38240                 template = new Roo.Template(
38241                    '<a href="#">' +
38242                    '<span unselectable="on"' +
38243                             (this.disableTooltips ? '' : ' title="{text}"') +
38244                             ' >{text}</span></a>'
38245                 );
38246             }
38247             
38248             switch (typeof(template)) {
38249                 case 'object' :
38250                     break;
38251                 case 'string' :
38252                     template = new Roo.Template(template);
38253                     break;
38254                 default :
38255                     break;
38256             }
38257             
38258             var el = template.overwrite(td, {"text": text});
38259             
38260             var inner = el.getElementsByTagName("span")[0];
38261             
38262             return {"el": el, "inner": inner};
38263             
38264     }
38265         
38266     
38267 });
38268
38269 /**
38270  * @class Roo.TabPanelItem
38271  * @extends Roo.util.Observable
38272  * Represents an individual item (tab plus body) in a TabPanel.
38273  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38274  * @param {String} id The id of this TabPanelItem
38275  * @param {String} text The text for the tab of this TabPanelItem
38276  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38277  */
38278 Roo.bootstrap.panel.TabItem = function(config){
38279     /**
38280      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38281      * @type Roo.TabPanel
38282      */
38283     this.tabPanel = config.panel;
38284     /**
38285      * The id for this TabPanelItem
38286      * @type String
38287      */
38288     this.id = config.id;
38289     /** @private */
38290     this.disabled = false;
38291     /** @private */
38292     this.text = config.text;
38293     /** @private */
38294     this.loaded = false;
38295     this.closable = config.closable;
38296
38297     /**
38298      * The body element for this TabPanelItem.
38299      * @type Roo.Element
38300      */
38301     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38302     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38303     this.bodyEl.setStyle("display", "block");
38304     this.bodyEl.setStyle("zoom", "1");
38305     //this.hideAction();
38306
38307     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38308     /** @private */
38309     this.el = Roo.get(els.el);
38310     this.inner = Roo.get(els.inner, true);
38311     this.textEl = Roo.get(this.el.dom.firstChild, true);
38312     this.pnode = Roo.get(els.el.parentNode, true);
38313 //    this.el.on("mousedown", this.onTabMouseDown, this);
38314     this.el.on("click", this.onTabClick, this);
38315     /** @private */
38316     if(config.closable){
38317         var c = Roo.get(els.close, true);
38318         c.dom.title = this.closeText;
38319         c.addClassOnOver("close-over");
38320         c.on("click", this.closeClick, this);
38321      }
38322
38323     this.addEvents({
38324          /**
38325          * @event activate
38326          * Fires when this tab becomes the active tab.
38327          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38328          * @param {Roo.TabPanelItem} this
38329          */
38330         "activate": true,
38331         /**
38332          * @event beforeclose
38333          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38334          * @param {Roo.TabPanelItem} this
38335          * @param {Object} e Set cancel to true on this object to cancel the close.
38336          */
38337         "beforeclose": true,
38338         /**
38339          * @event close
38340          * Fires when this tab is closed.
38341          * @param {Roo.TabPanelItem} this
38342          */
38343          "close": true,
38344         /**
38345          * @event deactivate
38346          * Fires when this tab is no longer the active tab.
38347          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38348          * @param {Roo.TabPanelItem} this
38349          */
38350          "deactivate" : true
38351     });
38352     this.hidden = false;
38353
38354     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38355 };
38356
38357 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38358            {
38359     purgeListeners : function(){
38360        Roo.util.Observable.prototype.purgeListeners.call(this);
38361        this.el.removeAllListeners();
38362     },
38363     /**
38364      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38365      */
38366     show : function(){
38367         this.pnode.addClass("active");
38368         this.showAction();
38369         if(Roo.isOpera){
38370             this.tabPanel.stripWrap.repaint();
38371         }
38372         this.fireEvent("activate", this.tabPanel, this);
38373     },
38374
38375     /**
38376      * Returns true if this tab is the active tab.
38377      * @return {Boolean}
38378      */
38379     isActive : function(){
38380         return this.tabPanel.getActiveTab() == this;
38381     },
38382
38383     /**
38384      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38385      */
38386     hide : function(){
38387         this.pnode.removeClass("active");
38388         this.hideAction();
38389         this.fireEvent("deactivate", this.tabPanel, this);
38390     },
38391
38392     hideAction : function(){
38393         this.bodyEl.hide();
38394         this.bodyEl.setStyle("position", "absolute");
38395         this.bodyEl.setLeft("-20000px");
38396         this.bodyEl.setTop("-20000px");
38397     },
38398
38399     showAction : function(){
38400         this.bodyEl.setStyle("position", "relative");
38401         this.bodyEl.setTop("");
38402         this.bodyEl.setLeft("");
38403         this.bodyEl.show();
38404     },
38405
38406     /**
38407      * Set the tooltip for the tab.
38408      * @param {String} tooltip The tab's tooltip
38409      */
38410     setTooltip : function(text){
38411         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38412             this.textEl.dom.qtip = text;
38413             this.textEl.dom.removeAttribute('title');
38414         }else{
38415             this.textEl.dom.title = text;
38416         }
38417     },
38418
38419     onTabClick : function(e){
38420         e.preventDefault();
38421         this.tabPanel.activate(this.id);
38422     },
38423
38424     onTabMouseDown : function(e){
38425         e.preventDefault();
38426         this.tabPanel.activate(this.id);
38427     },
38428 /*
38429     getWidth : function(){
38430         return this.inner.getWidth();
38431     },
38432
38433     setWidth : function(width){
38434         var iwidth = width - this.pnode.getPadding("lr");
38435         this.inner.setWidth(iwidth);
38436         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38437         this.pnode.setWidth(width);
38438     },
38439 */
38440     /**
38441      * Show or hide the tab
38442      * @param {Boolean} hidden True to hide or false to show.
38443      */
38444     setHidden : function(hidden){
38445         this.hidden = hidden;
38446         this.pnode.setStyle("display", hidden ? "none" : "");
38447     },
38448
38449     /**
38450      * Returns true if this tab is "hidden"
38451      * @return {Boolean}
38452      */
38453     isHidden : function(){
38454         return this.hidden;
38455     },
38456
38457     /**
38458      * Returns the text for this tab
38459      * @return {String}
38460      */
38461     getText : function(){
38462         return this.text;
38463     },
38464     /*
38465     autoSize : function(){
38466         //this.el.beginMeasure();
38467         this.textEl.setWidth(1);
38468         /*
38469          *  #2804 [new] Tabs in Roojs
38470          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38471          */
38472         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38473         //this.el.endMeasure();
38474     //},
38475
38476     /**
38477      * Sets the text for the tab (Note: this also sets the tooltip text)
38478      * @param {String} text The tab's text and tooltip
38479      */
38480     setText : function(text){
38481         this.text = text;
38482         this.textEl.update(text);
38483         this.setTooltip(text);
38484         //if(!this.tabPanel.resizeTabs){
38485         //    this.autoSize();
38486         //}
38487     },
38488     /**
38489      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38490      */
38491     activate : function(){
38492         this.tabPanel.activate(this.id);
38493     },
38494
38495     /**
38496      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38497      */
38498     disable : function(){
38499         if(this.tabPanel.active != this){
38500             this.disabled = true;
38501             this.pnode.addClass("disabled");
38502         }
38503     },
38504
38505     /**
38506      * Enables this TabPanelItem if it was previously disabled.
38507      */
38508     enable : function(){
38509         this.disabled = false;
38510         this.pnode.removeClass("disabled");
38511     },
38512
38513     /**
38514      * Sets the content for this TabPanelItem.
38515      * @param {String} content The content
38516      * @param {Boolean} loadScripts true to look for and load scripts
38517      */
38518     setContent : function(content, loadScripts){
38519         this.bodyEl.update(content, loadScripts);
38520     },
38521
38522     /**
38523      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38524      * @return {Roo.UpdateManager} The UpdateManager
38525      */
38526     getUpdateManager : function(){
38527         return this.bodyEl.getUpdateManager();
38528     },
38529
38530     /**
38531      * Set a URL to be used to load the content for this TabPanelItem.
38532      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38533      * @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)
38534      * @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)
38535      * @return {Roo.UpdateManager} The UpdateManager
38536      */
38537     setUrl : function(url, params, loadOnce){
38538         if(this.refreshDelegate){
38539             this.un('activate', this.refreshDelegate);
38540         }
38541         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38542         this.on("activate", this.refreshDelegate);
38543         return this.bodyEl.getUpdateManager();
38544     },
38545
38546     /** @private */
38547     _handleRefresh : function(url, params, loadOnce){
38548         if(!loadOnce || !this.loaded){
38549             var updater = this.bodyEl.getUpdateManager();
38550             updater.update(url, params, this._setLoaded.createDelegate(this));
38551         }
38552     },
38553
38554     /**
38555      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38556      *   Will fail silently if the setUrl method has not been called.
38557      *   This does not activate the panel, just updates its content.
38558      */
38559     refresh : function(){
38560         if(this.refreshDelegate){
38561            this.loaded = false;
38562            this.refreshDelegate();
38563         }
38564     },
38565
38566     /** @private */
38567     _setLoaded : function(){
38568         this.loaded = true;
38569     },
38570
38571     /** @private */
38572     closeClick : function(e){
38573         var o = {};
38574         e.stopEvent();
38575         this.fireEvent("beforeclose", this, o);
38576         if(o.cancel !== true){
38577             this.tabPanel.removeTab(this.id);
38578         }
38579     },
38580     /**
38581      * The text displayed in the tooltip for the close icon.
38582      * @type String
38583      */
38584     closeText : "Close this tab"
38585 });
38586 /**
38587 *    This script refer to:
38588 *    Title: International Telephone Input
38589 *    Author: Jack O'Connor
38590 *    Code version:  v12.1.12
38591 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38592 **/
38593
38594 Roo.bootstrap.PhoneInputData = function() {
38595     var d = [
38596       [
38597         "Afghanistan (‫افغانستان‬‎)",
38598         "af",
38599         "93"
38600       ],
38601       [
38602         "Albania (Shqipëri)",
38603         "al",
38604         "355"
38605       ],
38606       [
38607         "Algeria (‫الجزائر‬‎)",
38608         "dz",
38609         "213"
38610       ],
38611       [
38612         "American Samoa",
38613         "as",
38614         "1684"
38615       ],
38616       [
38617         "Andorra",
38618         "ad",
38619         "376"
38620       ],
38621       [
38622         "Angola",
38623         "ao",
38624         "244"
38625       ],
38626       [
38627         "Anguilla",
38628         "ai",
38629         "1264"
38630       ],
38631       [
38632         "Antigua and Barbuda",
38633         "ag",
38634         "1268"
38635       ],
38636       [
38637         "Argentina",
38638         "ar",
38639         "54"
38640       ],
38641       [
38642         "Armenia (Հայաստան)",
38643         "am",
38644         "374"
38645       ],
38646       [
38647         "Aruba",
38648         "aw",
38649         "297"
38650       ],
38651       [
38652         "Australia",
38653         "au",
38654         "61",
38655         0
38656       ],
38657       [
38658         "Austria (Österreich)",
38659         "at",
38660         "43"
38661       ],
38662       [
38663         "Azerbaijan (Azərbaycan)",
38664         "az",
38665         "994"
38666       ],
38667       [
38668         "Bahamas",
38669         "bs",
38670         "1242"
38671       ],
38672       [
38673         "Bahrain (‫البحرين‬‎)",
38674         "bh",
38675         "973"
38676       ],
38677       [
38678         "Bangladesh (বাংলাদেশ)",
38679         "bd",
38680         "880"
38681       ],
38682       [
38683         "Barbados",
38684         "bb",
38685         "1246"
38686       ],
38687       [
38688         "Belarus (Беларусь)",
38689         "by",
38690         "375"
38691       ],
38692       [
38693         "Belgium (België)",
38694         "be",
38695         "32"
38696       ],
38697       [
38698         "Belize",
38699         "bz",
38700         "501"
38701       ],
38702       [
38703         "Benin (Bénin)",
38704         "bj",
38705         "229"
38706       ],
38707       [
38708         "Bermuda",
38709         "bm",
38710         "1441"
38711       ],
38712       [
38713         "Bhutan (འབྲུག)",
38714         "bt",
38715         "975"
38716       ],
38717       [
38718         "Bolivia",
38719         "bo",
38720         "591"
38721       ],
38722       [
38723         "Bosnia and Herzegovina (Босна и Херцеговина)",
38724         "ba",
38725         "387"
38726       ],
38727       [
38728         "Botswana",
38729         "bw",
38730         "267"
38731       ],
38732       [
38733         "Brazil (Brasil)",
38734         "br",
38735         "55"
38736       ],
38737       [
38738         "British Indian Ocean Territory",
38739         "io",
38740         "246"
38741       ],
38742       [
38743         "British Virgin Islands",
38744         "vg",
38745         "1284"
38746       ],
38747       [
38748         "Brunei",
38749         "bn",
38750         "673"
38751       ],
38752       [
38753         "Bulgaria (България)",
38754         "bg",
38755         "359"
38756       ],
38757       [
38758         "Burkina Faso",
38759         "bf",
38760         "226"
38761       ],
38762       [
38763         "Burundi (Uburundi)",
38764         "bi",
38765         "257"
38766       ],
38767       [
38768         "Cambodia (កម្ពុជា)",
38769         "kh",
38770         "855"
38771       ],
38772       [
38773         "Cameroon (Cameroun)",
38774         "cm",
38775         "237"
38776       ],
38777       [
38778         "Canada",
38779         "ca",
38780         "1",
38781         1,
38782         ["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"]
38783       ],
38784       [
38785         "Cape Verde (Kabu Verdi)",
38786         "cv",
38787         "238"
38788       ],
38789       [
38790         "Caribbean Netherlands",
38791         "bq",
38792         "599",
38793         1
38794       ],
38795       [
38796         "Cayman Islands",
38797         "ky",
38798         "1345"
38799       ],
38800       [
38801         "Central African Republic (République centrafricaine)",
38802         "cf",
38803         "236"
38804       ],
38805       [
38806         "Chad (Tchad)",
38807         "td",
38808         "235"
38809       ],
38810       [
38811         "Chile",
38812         "cl",
38813         "56"
38814       ],
38815       [
38816         "China (中国)",
38817         "cn",
38818         "86"
38819       ],
38820       [
38821         "Christmas Island",
38822         "cx",
38823         "61",
38824         2
38825       ],
38826       [
38827         "Cocos (Keeling) Islands",
38828         "cc",
38829         "61",
38830         1
38831       ],
38832       [
38833         "Colombia",
38834         "co",
38835         "57"
38836       ],
38837       [
38838         "Comoros (‫جزر القمر‬‎)",
38839         "km",
38840         "269"
38841       ],
38842       [
38843         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38844         "cd",
38845         "243"
38846       ],
38847       [
38848         "Congo (Republic) (Congo-Brazzaville)",
38849         "cg",
38850         "242"
38851       ],
38852       [
38853         "Cook Islands",
38854         "ck",
38855         "682"
38856       ],
38857       [
38858         "Costa Rica",
38859         "cr",
38860         "506"
38861       ],
38862       [
38863         "Côte d’Ivoire",
38864         "ci",
38865         "225"
38866       ],
38867       [
38868         "Croatia (Hrvatska)",
38869         "hr",
38870         "385"
38871       ],
38872       [
38873         "Cuba",
38874         "cu",
38875         "53"
38876       ],
38877       [
38878         "Curaçao",
38879         "cw",
38880         "599",
38881         0
38882       ],
38883       [
38884         "Cyprus (Κύπρος)",
38885         "cy",
38886         "357"
38887       ],
38888       [
38889         "Czech Republic (Česká republika)",
38890         "cz",
38891         "420"
38892       ],
38893       [
38894         "Denmark (Danmark)",
38895         "dk",
38896         "45"
38897       ],
38898       [
38899         "Djibouti",
38900         "dj",
38901         "253"
38902       ],
38903       [
38904         "Dominica",
38905         "dm",
38906         "1767"
38907       ],
38908       [
38909         "Dominican Republic (República Dominicana)",
38910         "do",
38911         "1",
38912         2,
38913         ["809", "829", "849"]
38914       ],
38915       [
38916         "Ecuador",
38917         "ec",
38918         "593"
38919       ],
38920       [
38921         "Egypt (‫مصر‬‎)",
38922         "eg",
38923         "20"
38924       ],
38925       [
38926         "El Salvador",
38927         "sv",
38928         "503"
38929       ],
38930       [
38931         "Equatorial Guinea (Guinea Ecuatorial)",
38932         "gq",
38933         "240"
38934       ],
38935       [
38936         "Eritrea",
38937         "er",
38938         "291"
38939       ],
38940       [
38941         "Estonia (Eesti)",
38942         "ee",
38943         "372"
38944       ],
38945       [
38946         "Ethiopia",
38947         "et",
38948         "251"
38949       ],
38950       [
38951         "Falkland Islands (Islas Malvinas)",
38952         "fk",
38953         "500"
38954       ],
38955       [
38956         "Faroe Islands (Føroyar)",
38957         "fo",
38958         "298"
38959       ],
38960       [
38961         "Fiji",
38962         "fj",
38963         "679"
38964       ],
38965       [
38966         "Finland (Suomi)",
38967         "fi",
38968         "358",
38969         0
38970       ],
38971       [
38972         "France",
38973         "fr",
38974         "33"
38975       ],
38976       [
38977         "French Guiana (Guyane française)",
38978         "gf",
38979         "594"
38980       ],
38981       [
38982         "French Polynesia (Polynésie française)",
38983         "pf",
38984         "689"
38985       ],
38986       [
38987         "Gabon",
38988         "ga",
38989         "241"
38990       ],
38991       [
38992         "Gambia",
38993         "gm",
38994         "220"
38995       ],
38996       [
38997         "Georgia (საქართველო)",
38998         "ge",
38999         "995"
39000       ],
39001       [
39002         "Germany (Deutschland)",
39003         "de",
39004         "49"
39005       ],
39006       [
39007         "Ghana (Gaana)",
39008         "gh",
39009         "233"
39010       ],
39011       [
39012         "Gibraltar",
39013         "gi",
39014         "350"
39015       ],
39016       [
39017         "Greece (Ελλάδα)",
39018         "gr",
39019         "30"
39020       ],
39021       [
39022         "Greenland (Kalaallit Nunaat)",
39023         "gl",
39024         "299"
39025       ],
39026       [
39027         "Grenada",
39028         "gd",
39029         "1473"
39030       ],
39031       [
39032         "Guadeloupe",
39033         "gp",
39034         "590",
39035         0
39036       ],
39037       [
39038         "Guam",
39039         "gu",
39040         "1671"
39041       ],
39042       [
39043         "Guatemala",
39044         "gt",
39045         "502"
39046       ],
39047       [
39048         "Guernsey",
39049         "gg",
39050         "44",
39051         1
39052       ],
39053       [
39054         "Guinea (Guinée)",
39055         "gn",
39056         "224"
39057       ],
39058       [
39059         "Guinea-Bissau (Guiné Bissau)",
39060         "gw",
39061         "245"
39062       ],
39063       [
39064         "Guyana",
39065         "gy",
39066         "592"
39067       ],
39068       [
39069         "Haiti",
39070         "ht",
39071         "509"
39072       ],
39073       [
39074         "Honduras",
39075         "hn",
39076         "504"
39077       ],
39078       [
39079         "Hong Kong (香港)",
39080         "hk",
39081         "852"
39082       ],
39083       [
39084         "Hungary (Magyarország)",
39085         "hu",
39086         "36"
39087       ],
39088       [
39089         "Iceland (Ísland)",
39090         "is",
39091         "354"
39092       ],
39093       [
39094         "India (भारत)",
39095         "in",
39096         "91"
39097       ],
39098       [
39099         "Indonesia",
39100         "id",
39101         "62"
39102       ],
39103       [
39104         "Iran (‫ایران‬‎)",
39105         "ir",
39106         "98"
39107       ],
39108       [
39109         "Iraq (‫العراق‬‎)",
39110         "iq",
39111         "964"
39112       ],
39113       [
39114         "Ireland",
39115         "ie",
39116         "353"
39117       ],
39118       [
39119         "Isle of Man",
39120         "im",
39121         "44",
39122         2
39123       ],
39124       [
39125         "Israel (‫ישראל‬‎)",
39126         "il",
39127         "972"
39128       ],
39129       [
39130         "Italy (Italia)",
39131         "it",
39132         "39",
39133         0
39134       ],
39135       [
39136         "Jamaica",
39137         "jm",
39138         "1876"
39139       ],
39140       [
39141         "Japan (日本)",
39142         "jp",
39143         "81"
39144       ],
39145       [
39146         "Jersey",
39147         "je",
39148         "44",
39149         3
39150       ],
39151       [
39152         "Jordan (‫الأردن‬‎)",
39153         "jo",
39154         "962"
39155       ],
39156       [
39157         "Kazakhstan (Казахстан)",
39158         "kz",
39159         "7",
39160         1
39161       ],
39162       [
39163         "Kenya",
39164         "ke",
39165         "254"
39166       ],
39167       [
39168         "Kiribati",
39169         "ki",
39170         "686"
39171       ],
39172       [
39173         "Kosovo",
39174         "xk",
39175         "383"
39176       ],
39177       [
39178         "Kuwait (‫الكويت‬‎)",
39179         "kw",
39180         "965"
39181       ],
39182       [
39183         "Kyrgyzstan (Кыргызстан)",
39184         "kg",
39185         "996"
39186       ],
39187       [
39188         "Laos (ລາວ)",
39189         "la",
39190         "856"
39191       ],
39192       [
39193         "Latvia (Latvija)",
39194         "lv",
39195         "371"
39196       ],
39197       [
39198         "Lebanon (‫لبنان‬‎)",
39199         "lb",
39200         "961"
39201       ],
39202       [
39203         "Lesotho",
39204         "ls",
39205         "266"
39206       ],
39207       [
39208         "Liberia",
39209         "lr",
39210         "231"
39211       ],
39212       [
39213         "Libya (‫ليبيا‬‎)",
39214         "ly",
39215         "218"
39216       ],
39217       [
39218         "Liechtenstein",
39219         "li",
39220         "423"
39221       ],
39222       [
39223         "Lithuania (Lietuva)",
39224         "lt",
39225         "370"
39226       ],
39227       [
39228         "Luxembourg",
39229         "lu",
39230         "352"
39231       ],
39232       [
39233         "Macau (澳門)",
39234         "mo",
39235         "853"
39236       ],
39237       [
39238         "Macedonia (FYROM) (Македонија)",
39239         "mk",
39240         "389"
39241       ],
39242       [
39243         "Madagascar (Madagasikara)",
39244         "mg",
39245         "261"
39246       ],
39247       [
39248         "Malawi",
39249         "mw",
39250         "265"
39251       ],
39252       [
39253         "Malaysia",
39254         "my",
39255         "60"
39256       ],
39257       [
39258         "Maldives",
39259         "mv",
39260         "960"
39261       ],
39262       [
39263         "Mali",
39264         "ml",
39265         "223"
39266       ],
39267       [
39268         "Malta",
39269         "mt",
39270         "356"
39271       ],
39272       [
39273         "Marshall Islands",
39274         "mh",
39275         "692"
39276       ],
39277       [
39278         "Martinique",
39279         "mq",
39280         "596"
39281       ],
39282       [
39283         "Mauritania (‫موريتانيا‬‎)",
39284         "mr",
39285         "222"
39286       ],
39287       [
39288         "Mauritius (Moris)",
39289         "mu",
39290         "230"
39291       ],
39292       [
39293         "Mayotte",
39294         "yt",
39295         "262",
39296         1
39297       ],
39298       [
39299         "Mexico (México)",
39300         "mx",
39301         "52"
39302       ],
39303       [
39304         "Micronesia",
39305         "fm",
39306         "691"
39307       ],
39308       [
39309         "Moldova (Republica Moldova)",
39310         "md",
39311         "373"
39312       ],
39313       [
39314         "Monaco",
39315         "mc",
39316         "377"
39317       ],
39318       [
39319         "Mongolia (Монгол)",
39320         "mn",
39321         "976"
39322       ],
39323       [
39324         "Montenegro (Crna Gora)",
39325         "me",
39326         "382"
39327       ],
39328       [
39329         "Montserrat",
39330         "ms",
39331         "1664"
39332       ],
39333       [
39334         "Morocco (‫المغرب‬‎)",
39335         "ma",
39336         "212",
39337         0
39338       ],
39339       [
39340         "Mozambique (Moçambique)",
39341         "mz",
39342         "258"
39343       ],
39344       [
39345         "Myanmar (Burma) (မြန်မာ)",
39346         "mm",
39347         "95"
39348       ],
39349       [
39350         "Namibia (Namibië)",
39351         "na",
39352         "264"
39353       ],
39354       [
39355         "Nauru",
39356         "nr",
39357         "674"
39358       ],
39359       [
39360         "Nepal (नेपाल)",
39361         "np",
39362         "977"
39363       ],
39364       [
39365         "Netherlands (Nederland)",
39366         "nl",
39367         "31"
39368       ],
39369       [
39370         "New Caledonia (Nouvelle-Calédonie)",
39371         "nc",
39372         "687"
39373       ],
39374       [
39375         "New Zealand",
39376         "nz",
39377         "64"
39378       ],
39379       [
39380         "Nicaragua",
39381         "ni",
39382         "505"
39383       ],
39384       [
39385         "Niger (Nijar)",
39386         "ne",
39387         "227"
39388       ],
39389       [
39390         "Nigeria",
39391         "ng",
39392         "234"
39393       ],
39394       [
39395         "Niue",
39396         "nu",
39397         "683"
39398       ],
39399       [
39400         "Norfolk Island",
39401         "nf",
39402         "672"
39403       ],
39404       [
39405         "North Korea (조선 민주주의 인민 공화국)",
39406         "kp",
39407         "850"
39408       ],
39409       [
39410         "Northern Mariana Islands",
39411         "mp",
39412         "1670"
39413       ],
39414       [
39415         "Norway (Norge)",
39416         "no",
39417         "47",
39418         0
39419       ],
39420       [
39421         "Oman (‫عُمان‬‎)",
39422         "om",
39423         "968"
39424       ],
39425       [
39426         "Pakistan (‫پاکستان‬‎)",
39427         "pk",
39428         "92"
39429       ],
39430       [
39431         "Palau",
39432         "pw",
39433         "680"
39434       ],
39435       [
39436         "Palestine (‫فلسطين‬‎)",
39437         "ps",
39438         "970"
39439       ],
39440       [
39441         "Panama (Panamá)",
39442         "pa",
39443         "507"
39444       ],
39445       [
39446         "Papua New Guinea",
39447         "pg",
39448         "675"
39449       ],
39450       [
39451         "Paraguay",
39452         "py",
39453         "595"
39454       ],
39455       [
39456         "Peru (Perú)",
39457         "pe",
39458         "51"
39459       ],
39460       [
39461         "Philippines",
39462         "ph",
39463         "63"
39464       ],
39465       [
39466         "Poland (Polska)",
39467         "pl",
39468         "48"
39469       ],
39470       [
39471         "Portugal",
39472         "pt",
39473         "351"
39474       ],
39475       [
39476         "Puerto Rico",
39477         "pr",
39478         "1",
39479         3,
39480         ["787", "939"]
39481       ],
39482       [
39483         "Qatar (‫قطر‬‎)",
39484         "qa",
39485         "974"
39486       ],
39487       [
39488         "Réunion (La Réunion)",
39489         "re",
39490         "262",
39491         0
39492       ],
39493       [
39494         "Romania (România)",
39495         "ro",
39496         "40"
39497       ],
39498       [
39499         "Russia (Россия)",
39500         "ru",
39501         "7",
39502         0
39503       ],
39504       [
39505         "Rwanda",
39506         "rw",
39507         "250"
39508       ],
39509       [
39510         "Saint Barthélemy",
39511         "bl",
39512         "590",
39513         1
39514       ],
39515       [
39516         "Saint Helena",
39517         "sh",
39518         "290"
39519       ],
39520       [
39521         "Saint Kitts and Nevis",
39522         "kn",
39523         "1869"
39524       ],
39525       [
39526         "Saint Lucia",
39527         "lc",
39528         "1758"
39529       ],
39530       [
39531         "Saint Martin (Saint-Martin (partie française))",
39532         "mf",
39533         "590",
39534         2
39535       ],
39536       [
39537         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39538         "pm",
39539         "508"
39540       ],
39541       [
39542         "Saint Vincent and the Grenadines",
39543         "vc",
39544         "1784"
39545       ],
39546       [
39547         "Samoa",
39548         "ws",
39549         "685"
39550       ],
39551       [
39552         "San Marino",
39553         "sm",
39554         "378"
39555       ],
39556       [
39557         "São Tomé and Príncipe (São Tomé e Príncipe)",
39558         "st",
39559         "239"
39560       ],
39561       [
39562         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39563         "sa",
39564         "966"
39565       ],
39566       [
39567         "Senegal (Sénégal)",
39568         "sn",
39569         "221"
39570       ],
39571       [
39572         "Serbia (Србија)",
39573         "rs",
39574         "381"
39575       ],
39576       [
39577         "Seychelles",
39578         "sc",
39579         "248"
39580       ],
39581       [
39582         "Sierra Leone",
39583         "sl",
39584         "232"
39585       ],
39586       [
39587         "Singapore",
39588         "sg",
39589         "65"
39590       ],
39591       [
39592         "Sint Maarten",
39593         "sx",
39594         "1721"
39595       ],
39596       [
39597         "Slovakia (Slovensko)",
39598         "sk",
39599         "421"
39600       ],
39601       [
39602         "Slovenia (Slovenija)",
39603         "si",
39604         "386"
39605       ],
39606       [
39607         "Solomon Islands",
39608         "sb",
39609         "677"
39610       ],
39611       [
39612         "Somalia (Soomaaliya)",
39613         "so",
39614         "252"
39615       ],
39616       [
39617         "South Africa",
39618         "za",
39619         "27"
39620       ],
39621       [
39622         "South Korea (대한민국)",
39623         "kr",
39624         "82"
39625       ],
39626       [
39627         "South Sudan (‫جنوب السودان‬‎)",
39628         "ss",
39629         "211"
39630       ],
39631       [
39632         "Spain (España)",
39633         "es",
39634         "34"
39635       ],
39636       [
39637         "Sri Lanka (ශ්‍රී ලංකාව)",
39638         "lk",
39639         "94"
39640       ],
39641       [
39642         "Sudan (‫السودان‬‎)",
39643         "sd",
39644         "249"
39645       ],
39646       [
39647         "Suriname",
39648         "sr",
39649         "597"
39650       ],
39651       [
39652         "Svalbard and Jan Mayen",
39653         "sj",
39654         "47",
39655         1
39656       ],
39657       [
39658         "Swaziland",
39659         "sz",
39660         "268"
39661       ],
39662       [
39663         "Sweden (Sverige)",
39664         "se",
39665         "46"
39666       ],
39667       [
39668         "Switzerland (Schweiz)",
39669         "ch",
39670         "41"
39671       ],
39672       [
39673         "Syria (‫سوريا‬‎)",
39674         "sy",
39675         "963"
39676       ],
39677       [
39678         "Taiwan (台灣)",
39679         "tw",
39680         "886"
39681       ],
39682       [
39683         "Tajikistan",
39684         "tj",
39685         "992"
39686       ],
39687       [
39688         "Tanzania",
39689         "tz",
39690         "255"
39691       ],
39692       [
39693         "Thailand (ไทย)",
39694         "th",
39695         "66"
39696       ],
39697       [
39698         "Timor-Leste",
39699         "tl",
39700         "670"
39701       ],
39702       [
39703         "Togo",
39704         "tg",
39705         "228"
39706       ],
39707       [
39708         "Tokelau",
39709         "tk",
39710         "690"
39711       ],
39712       [
39713         "Tonga",
39714         "to",
39715         "676"
39716       ],
39717       [
39718         "Trinidad and Tobago",
39719         "tt",
39720         "1868"
39721       ],
39722       [
39723         "Tunisia (‫تونس‬‎)",
39724         "tn",
39725         "216"
39726       ],
39727       [
39728         "Turkey (Türkiye)",
39729         "tr",
39730         "90"
39731       ],
39732       [
39733         "Turkmenistan",
39734         "tm",
39735         "993"
39736       ],
39737       [
39738         "Turks and Caicos Islands",
39739         "tc",
39740         "1649"
39741       ],
39742       [
39743         "Tuvalu",
39744         "tv",
39745         "688"
39746       ],
39747       [
39748         "U.S. Virgin Islands",
39749         "vi",
39750         "1340"
39751       ],
39752       [
39753         "Uganda",
39754         "ug",
39755         "256"
39756       ],
39757       [
39758         "Ukraine (Україна)",
39759         "ua",
39760         "380"
39761       ],
39762       [
39763         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39764         "ae",
39765         "971"
39766       ],
39767       [
39768         "United Kingdom",
39769         "gb",
39770         "44",
39771         0
39772       ],
39773       [
39774         "United States",
39775         "us",
39776         "1",
39777         0
39778       ],
39779       [
39780         "Uruguay",
39781         "uy",
39782         "598"
39783       ],
39784       [
39785         "Uzbekistan (Oʻzbekiston)",
39786         "uz",
39787         "998"
39788       ],
39789       [
39790         "Vanuatu",
39791         "vu",
39792         "678"
39793       ],
39794       [
39795         "Vatican City (Città del Vaticano)",
39796         "va",
39797         "39",
39798         1
39799       ],
39800       [
39801         "Venezuela",
39802         "ve",
39803         "58"
39804       ],
39805       [
39806         "Vietnam (Việt Nam)",
39807         "vn",
39808         "84"
39809       ],
39810       [
39811         "Wallis and Futuna (Wallis-et-Futuna)",
39812         "wf",
39813         "681"
39814       ],
39815       [
39816         "Western Sahara (‫الصحراء الغربية‬‎)",
39817         "eh",
39818         "212",
39819         1
39820       ],
39821       [
39822         "Yemen (‫اليمن‬‎)",
39823         "ye",
39824         "967"
39825       ],
39826       [
39827         "Zambia",
39828         "zm",
39829         "260"
39830       ],
39831       [
39832         "Zimbabwe",
39833         "zw",
39834         "263"
39835       ],
39836       [
39837         "Åland Islands",
39838         "ax",
39839         "358",
39840         1
39841       ]
39842   ];
39843   
39844   return d;
39845 }/**
39846 *    This script refer to:
39847 *    Title: International Telephone Input
39848 *    Author: Jack O'Connor
39849 *    Code version:  v12.1.12
39850 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39851 **/
39852
39853 /**
39854  * @class Roo.bootstrap.PhoneInput
39855  * @extends Roo.bootstrap.TriggerField
39856  * An input with International dial-code selection
39857  
39858  * @cfg {String} defaultDialCode default '+852'
39859  * @cfg {Array} preferedCountries default []
39860   
39861  * @constructor
39862  * Create a new PhoneInput.
39863  * @param {Object} config Configuration options
39864  */
39865
39866 Roo.bootstrap.PhoneInput = function(config) {
39867     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39868 };
39869
39870 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39871         
39872         listWidth: undefined,
39873         
39874         selectedClass: 'active',
39875         
39876         invalidClass : "has-warning",
39877         
39878         validClass: 'has-success',
39879         
39880         allowed: '0123456789',
39881         
39882         /**
39883          * @cfg {String} defaultDialCode The default dial code when initializing the input
39884          */
39885         defaultDialCode: '+852',
39886         
39887         /**
39888          * @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
39889          */
39890         preferedCountries: false,
39891         
39892         getAutoCreate : function()
39893         {
39894             var data = Roo.bootstrap.PhoneInputData();
39895             var align = this.labelAlign || this.parentLabelAlign();
39896             var id = Roo.id();
39897             
39898             this.allCountries = [];
39899             this.dialCodeMapping = [];
39900             
39901             for (var i = 0; i < data.length; i++) {
39902               var c = data[i];
39903               this.allCountries[i] = {
39904                 name: c[0],
39905                 iso2: c[1],
39906                 dialCode: c[2],
39907                 priority: c[3] || 0,
39908                 areaCodes: c[4] || null
39909               };
39910               this.dialCodeMapping[c[2]] = {
39911                   name: c[0],
39912                   iso2: c[1],
39913                   priority: c[3] || 0,
39914                   areaCodes: c[4] || null
39915               };
39916             }
39917             
39918             var cfg = {
39919                 cls: 'form-group',
39920                 cn: []
39921             };
39922             
39923             var input =  {
39924                 tag: 'input',
39925                 id : id,
39926                 cls : 'form-control tel-input',
39927                 autocomplete: 'new-password'
39928             };
39929             
39930             var hiddenInput = {
39931                 tag: 'input',
39932                 type: 'hidden',
39933                 cls: 'hidden-tel-input'
39934             };
39935             
39936             if (this.name) {
39937                 hiddenInput.name = this.name;
39938             }
39939             
39940             if (this.disabled) {
39941                 input.disabled = true;
39942             }
39943             
39944             var flag_container = {
39945                 tag: 'div',
39946                 cls: 'flag-box',
39947                 cn: [
39948                     {
39949                         tag: 'div',
39950                         cls: 'flag'
39951                     },
39952                     {
39953                         tag: 'div',
39954                         cls: 'caret'
39955                     }
39956                 ]
39957             };
39958             
39959             var box = {
39960                 tag: 'div',
39961                 cls: this.hasFeedback ? 'has-feedback' : '',
39962                 cn: [
39963                     hiddenInput,
39964                     input,
39965                     {
39966                         tag: 'input',
39967                         cls: 'dial-code-holder',
39968                         disabled: true
39969                     }
39970                 ]
39971             };
39972             
39973             var container = {
39974                 cls: 'roo-select2-container input-group',
39975                 cn: [
39976                     flag_container,
39977                     box
39978                 ]
39979             };
39980             
39981             if (this.fieldLabel.length) {
39982                 var indicator = {
39983                     tag: 'i',
39984                     tooltip: 'This field is required'
39985                 };
39986                 
39987                 var label = {
39988                     tag: 'label',
39989                     'for':  id,
39990                     cls: 'control-label',
39991                     cn: []
39992                 };
39993                 
39994                 var label_text = {
39995                     tag: 'span',
39996                     html: this.fieldLabel
39997                 };
39998                 
39999                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40000                 label.cn = [
40001                     indicator,
40002                     label_text
40003                 ];
40004                 
40005                 if(this.indicatorpos == 'right') {
40006                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40007                     label.cn = [
40008                         label_text,
40009                         indicator
40010                     ];
40011                 }
40012                 
40013                 if(align == 'left') {
40014                     container = {
40015                         tag: 'div',
40016                         cn: [
40017                             container
40018                         ]
40019                     };
40020                     
40021                     if(this.labelWidth > 12){
40022                         label.style = "width: " + this.labelWidth + 'px';
40023                     }
40024                     if(this.labelWidth < 13 && this.labelmd == 0){
40025                         this.labelmd = this.labelWidth;
40026                     }
40027                     if(this.labellg > 0){
40028                         label.cls += ' col-lg-' + this.labellg;
40029                         input.cls += ' col-lg-' + (12 - this.labellg);
40030                     }
40031                     if(this.labelmd > 0){
40032                         label.cls += ' col-md-' + this.labelmd;
40033                         container.cls += ' col-md-' + (12 - this.labelmd);
40034                     }
40035                     if(this.labelsm > 0){
40036                         label.cls += ' col-sm-' + this.labelsm;
40037                         container.cls += ' col-sm-' + (12 - this.labelsm);
40038                     }
40039                     if(this.labelxs > 0){
40040                         label.cls += ' col-xs-' + this.labelxs;
40041                         container.cls += ' col-xs-' + (12 - this.labelxs);
40042                     }
40043                 }
40044             }
40045             
40046             cfg.cn = [
40047                 label,
40048                 container
40049             ];
40050             
40051             var settings = this;
40052             
40053             ['xs','sm','md','lg'].map(function(size){
40054                 if (settings[size]) {
40055                     cfg.cls += ' col-' + size + '-' + settings[size];
40056                 }
40057             });
40058             
40059             this.store = new Roo.data.Store({
40060                 proxy : new Roo.data.MemoryProxy({}),
40061                 reader : new Roo.data.JsonReader({
40062                     fields : [
40063                         {
40064                             'name' : 'name',
40065                             'type' : 'string'
40066                         },
40067                         {
40068                             'name' : 'iso2',
40069                             'type' : 'string'
40070                         },
40071                         {
40072                             'name' : 'dialCode',
40073                             'type' : 'string'
40074                         },
40075                         {
40076                             'name' : 'priority',
40077                             'type' : 'string'
40078                         },
40079                         {
40080                             'name' : 'areaCodes',
40081                             'type' : 'string'
40082                         }
40083                     ]
40084                 })
40085             });
40086             
40087             if(!this.preferedCountries) {
40088                 this.preferedCountries = [
40089                     'hk',
40090                     'gb',
40091                     'us'
40092                 ];
40093             }
40094             
40095             var p = this.preferedCountries.reverse();
40096             
40097             if(p) {
40098                 for (var i = 0; i < p.length; i++) {
40099                     for (var j = 0; j < this.allCountries.length; j++) {
40100                         if(this.allCountries[j].iso2 == p[i]) {
40101                             var t = this.allCountries[j];
40102                             this.allCountries.splice(j,1);
40103                             this.allCountries.unshift(t);
40104                         }
40105                     } 
40106                 }
40107             }
40108             
40109             this.store.proxy.data = {
40110                 success: true,
40111                 data: this.allCountries
40112             };
40113             
40114             return cfg;
40115         },
40116         
40117         initEvents : function()
40118         {
40119             this.createList();
40120             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40121             
40122             this.indicator = this.indicatorEl();
40123             this.flag = this.flagEl();
40124             this.dialCodeHolder = this.dialCodeHolderEl();
40125             
40126             this.trigger = this.el.select('div.flag-box',true).first();
40127             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40128             
40129             var _this = this;
40130             
40131             (function(){
40132                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40133                 _this.list.setWidth(lw);
40134             }).defer(100);
40135             
40136             this.list.on('mouseover', this.onViewOver, this);
40137             this.list.on('mousemove', this.onViewMove, this);
40138             this.inputEl().on("keyup", this.onKeyUp, this);
40139             
40140             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40141
40142             this.view = new Roo.View(this.list, this.tpl, {
40143                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40144             });
40145             
40146             this.view.on('click', this.onViewClick, this);
40147             this.setValue(this.defaultDialCode);
40148         },
40149         
40150         onTriggerClick : function(e)
40151         {
40152             Roo.log('trigger click');
40153             if(this.disabled){
40154                 return;
40155             }
40156             
40157             if(this.isExpanded()){
40158                 this.collapse();
40159                 this.hasFocus = false;
40160             }else {
40161                 this.store.load({});
40162                 this.hasFocus = true;
40163                 this.expand();
40164             }
40165         },
40166         
40167         isExpanded : function()
40168         {
40169             return this.list.isVisible();
40170         },
40171         
40172         collapse : function()
40173         {
40174             if(!this.isExpanded()){
40175                 return;
40176             }
40177             this.list.hide();
40178             Roo.get(document).un('mousedown', this.collapseIf, this);
40179             Roo.get(document).un('mousewheel', this.collapseIf, this);
40180             this.fireEvent('collapse', this);
40181             this.validate();
40182         },
40183         
40184         expand : function()
40185         {
40186             Roo.log('expand');
40187
40188             if(this.isExpanded() || !this.hasFocus){
40189                 return;
40190             }
40191             
40192             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40193             this.list.setWidth(lw);
40194             
40195             this.list.show();
40196             this.restrictHeight();
40197             
40198             Roo.get(document).on('mousedown', this.collapseIf, this);
40199             Roo.get(document).on('mousewheel', this.collapseIf, this);
40200             
40201             this.fireEvent('expand', this);
40202         },
40203         
40204         restrictHeight : function()
40205         {
40206             this.list.alignTo(this.inputEl(), this.listAlign);
40207             this.list.alignTo(this.inputEl(), this.listAlign);
40208         },
40209         
40210         onViewOver : function(e, t)
40211         {
40212             if(this.inKeyMode){
40213                 return;
40214             }
40215             var item = this.view.findItemFromChild(t);
40216             
40217             if(item){
40218                 var index = this.view.indexOf(item);
40219                 this.select(index, false);
40220             }
40221         },
40222
40223         // private
40224         onViewClick : function(view, doFocus, el, e)
40225         {
40226             var index = this.view.getSelectedIndexes()[0];
40227             
40228             var r = this.store.getAt(index);
40229             
40230             if(r){
40231                 this.onSelect(r, index);
40232             }
40233             if(doFocus !== false && !this.blockFocus){
40234                 this.inputEl().focus();
40235             }
40236         },
40237         
40238         onViewMove : function(e, t)
40239         {
40240             this.inKeyMode = false;
40241         },
40242         
40243         select : function(index, scrollIntoView)
40244         {
40245             this.selectedIndex = index;
40246             this.view.select(index);
40247             if(scrollIntoView !== false){
40248                 var el = this.view.getNode(index);
40249                 if(el){
40250                     this.list.scrollChildIntoView(el, false);
40251                 }
40252             }
40253         },
40254         
40255         createList : function()
40256         {
40257             this.list = Roo.get(document.body).createChild({
40258                 tag: 'ul',
40259                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40260                 style: 'display:none'
40261             });
40262             
40263             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40264         },
40265         
40266         collapseIf : function(e)
40267         {
40268             var in_combo  = e.within(this.el);
40269             var in_list =  e.within(this.list);
40270             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40271             
40272             if (in_combo || in_list || is_list) {
40273                 return;
40274             }
40275             this.collapse();
40276         },
40277         
40278         onSelect : function(record, index)
40279         {
40280             if(this.fireEvent('beforeselect', this, record, index) !== false){
40281                 
40282                 this.setFlagClass(record.data.iso2);
40283                 this.setDialCode(record.data.dialCode);
40284                 this.hasFocus = false;
40285                 this.collapse();
40286                 this.fireEvent('select', this, record, index);
40287             }
40288         },
40289         
40290         flagEl : function()
40291         {
40292             var flag = this.el.select('div.flag',true).first();
40293             if(!flag){
40294                 return false;
40295             }
40296             return flag;
40297         },
40298         
40299         dialCodeHolderEl : function()
40300         {
40301             var d = this.el.select('input.dial-code-holder',true).first();
40302             if(!d){
40303                 return false;
40304             }
40305             return d;
40306         },
40307         
40308         setDialCode : function(v)
40309         {
40310             this.dialCodeHolder.dom.value = '+'+v;
40311         },
40312         
40313         setFlagClass : function(n)
40314         {
40315             this.flag.dom.className = 'flag '+n;
40316         },
40317         
40318         getValue : function()
40319         {
40320             var v = this.inputEl().getValue();
40321             if(this.dialCodeHolder) {
40322                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40323             }
40324             return v;
40325         },
40326         
40327         setValue : function(v)
40328         {
40329             var d = this.getDialCode(v);
40330             
40331             //invalid dial code
40332             if(v.length == 0 || !d || d.length == 0) {
40333                 if(this.rendered){
40334                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40335                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40336                 }
40337                 return;
40338             }
40339             
40340             //valid dial code
40341             this.setFlagClass(this.dialCodeMapping[d].iso2);
40342             this.setDialCode(d);
40343             this.inputEl().dom.value = v.replace('+'+d,'');
40344             this.hiddenEl().dom.value = this.getValue();
40345             
40346             this.validate();
40347         },
40348         
40349         getDialCode : function(v)
40350         {
40351             v = v ||  '';
40352             
40353             if (v.length == 0) {
40354                 return this.dialCodeHolder.dom.value;
40355             }
40356             
40357             var dialCode = "";
40358             if (v.charAt(0) != "+") {
40359                 return false;
40360             }
40361             var numericChars = "";
40362             for (var i = 1; i < v.length; i++) {
40363               var c = v.charAt(i);
40364               if (!isNaN(c)) {
40365                 numericChars += c;
40366                 if (this.dialCodeMapping[numericChars]) {
40367                   dialCode = v.substr(1, i);
40368                 }
40369                 if (numericChars.length == 4) {
40370                   break;
40371                 }
40372               }
40373             }
40374             return dialCode;
40375         },
40376         
40377         reset : function()
40378         {
40379             this.setValue(this.defaultDialCode);
40380             this.validate();
40381         },
40382         
40383         hiddenEl : function()
40384         {
40385             return this.el.select('input.hidden-tel-input',true).first();
40386         },
40387         
40388         onKeyUp : function(e){
40389             
40390             var k = e.getKey();
40391             var c = e.getCharCode();
40392             
40393             if(
40394                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40395                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40396             ){
40397                 e.stopEvent();
40398             }
40399             
40400             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40401             //     return;
40402             // }
40403             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40404                 e.stopEvent();
40405             }
40406             
40407             this.setValue(this.getValue());
40408         }
40409         
40410 });
40411 /**
40412  * @class Roo.bootstrap.MoneyField
40413  * @extends Roo.bootstrap.ComboBox
40414  * Bootstrap MoneyField class
40415  * 
40416  * @constructor
40417  * Create a new MoneyField.
40418  * @param {Object} config Configuration options
40419  */
40420
40421 Roo.bootstrap.MoneyField = function(config) {
40422     
40423     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40424     
40425 };
40426
40427 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40428     
40429     /**
40430      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40431      */
40432     allowDecimals : true,
40433     /**
40434      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40435      */
40436     decimalSeparator : ".",
40437     /**
40438      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40439      */
40440     decimalPrecision : 0,
40441     /**
40442      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40443      */
40444     allowNegative : true,
40445     /**
40446      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40447      */
40448     allowZero: true,
40449     /**
40450      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40451      */
40452     minValue : Number.NEGATIVE_INFINITY,
40453     /**
40454      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40455      */
40456     maxValue : Number.MAX_VALUE,
40457     /**
40458      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40459      */
40460     minText : "The minimum value for this field is {0}",
40461     /**
40462      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40463      */
40464     maxText : "The maximum value for this field is {0}",
40465     /**
40466      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40467      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40468      */
40469     nanText : "{0} is not a valid number",
40470     /**
40471      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40472      */
40473     castInt : true,
40474     /**
40475      * @cfg {String} defaults currency of the MoneyField
40476      * value should be in lkey
40477      */
40478     defaultCurrency : false,
40479     /**
40480      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40481      */
40482     thousandsDelimiter : false,
40483     
40484     
40485     inputlg : 9,
40486     inputmd : 9,
40487     inputsm : 9,
40488     inputxs : 6,
40489     
40490     store : false,
40491     
40492     getAutoCreate : function()
40493     {
40494         var align = this.labelAlign || this.parentLabelAlign();
40495         
40496         var id = Roo.id();
40497
40498         var cfg = {
40499             cls: 'form-group',
40500             cn: []
40501         };
40502
40503         var input =  {
40504             tag: 'input',
40505             id : id,
40506             cls : 'form-control roo-money-amount-input',
40507             autocomplete: 'new-password'
40508         };
40509         
40510         var hiddenInput = {
40511             tag: 'input',
40512             type: 'hidden',
40513             id: Roo.id(),
40514             cls: 'hidden-number-input'
40515         };
40516         
40517         if (this.name) {
40518             hiddenInput.name = this.name;
40519         }
40520
40521         if (this.disabled) {
40522             input.disabled = true;
40523         }
40524
40525         var clg = 12 - this.inputlg;
40526         var cmd = 12 - this.inputmd;
40527         var csm = 12 - this.inputsm;
40528         var cxs = 12 - this.inputxs;
40529         
40530         var container = {
40531             tag : 'div',
40532             cls : 'row roo-money-field',
40533             cn : [
40534                 {
40535                     tag : 'div',
40536                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40537                     cn : [
40538                         {
40539                             tag : 'div',
40540                             cls: 'roo-select2-container input-group',
40541                             cn: [
40542                                 {
40543                                     tag : 'input',
40544                                     cls : 'form-control roo-money-currency-input',
40545                                     autocomplete: 'new-password',
40546                                     readOnly : 1,
40547                                     name : this.currencyName
40548                                 },
40549                                 {
40550                                     tag :'span',
40551                                     cls : 'input-group-addon',
40552                                     cn : [
40553                                         {
40554                                             tag: 'span',
40555                                             cls: 'caret'
40556                                         }
40557                                     ]
40558                                 }
40559                             ]
40560                         }
40561                     ]
40562                 },
40563                 {
40564                     tag : 'div',
40565                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40566                     cn : [
40567                         {
40568                             tag: 'div',
40569                             cls: this.hasFeedback ? 'has-feedback' : '',
40570                             cn: [
40571                                 input
40572                             ]
40573                         }
40574                     ]
40575                 }
40576             ]
40577             
40578         };
40579         
40580         if (this.fieldLabel.length) {
40581             var indicator = {
40582                 tag: 'i',
40583                 tooltip: 'This field is required'
40584             };
40585
40586             var label = {
40587                 tag: 'label',
40588                 'for':  id,
40589                 cls: 'control-label',
40590                 cn: []
40591             };
40592
40593             var label_text = {
40594                 tag: 'span',
40595                 html: this.fieldLabel
40596             };
40597
40598             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40599             label.cn = [
40600                 indicator,
40601                 label_text
40602             ];
40603
40604             if(this.indicatorpos == 'right') {
40605                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40606                 label.cn = [
40607                     label_text,
40608                     indicator
40609                 ];
40610             }
40611
40612             if(align == 'left') {
40613                 container = {
40614                     tag: 'div',
40615                     cn: [
40616                         container
40617                     ]
40618                 };
40619
40620                 if(this.labelWidth > 12){
40621                     label.style = "width: " + this.labelWidth + 'px';
40622                 }
40623                 if(this.labelWidth < 13 && this.labelmd == 0){
40624                     this.labelmd = this.labelWidth;
40625                 }
40626                 if(this.labellg > 0){
40627                     label.cls += ' col-lg-' + this.labellg;
40628                     input.cls += ' col-lg-' + (12 - this.labellg);
40629                 }
40630                 if(this.labelmd > 0){
40631                     label.cls += ' col-md-' + this.labelmd;
40632                     container.cls += ' col-md-' + (12 - this.labelmd);
40633                 }
40634                 if(this.labelsm > 0){
40635                     label.cls += ' col-sm-' + this.labelsm;
40636                     container.cls += ' col-sm-' + (12 - this.labelsm);
40637                 }
40638                 if(this.labelxs > 0){
40639                     label.cls += ' col-xs-' + this.labelxs;
40640                     container.cls += ' col-xs-' + (12 - this.labelxs);
40641                 }
40642             }
40643         }
40644
40645         cfg.cn = [
40646             label,
40647             container,
40648             hiddenInput
40649         ];
40650         
40651         var settings = this;
40652
40653         ['xs','sm','md','lg'].map(function(size){
40654             if (settings[size]) {
40655                 cfg.cls += ' col-' + size + '-' + settings[size];
40656             }
40657         });
40658         
40659         return cfg;
40660     },
40661     
40662     initEvents : function()
40663     {
40664         this.indicator = this.indicatorEl();
40665         
40666         this.initCurrencyEvent();
40667         
40668         this.initNumberEvent();
40669     },
40670     
40671     initCurrencyEvent : function()
40672     {
40673         if (!this.store) {
40674             throw "can not find store for combo";
40675         }
40676         
40677         this.store = Roo.factory(this.store, Roo.data);
40678         this.store.parent = this;
40679         
40680         this.createList();
40681         
40682         this.triggerEl = this.el.select('.input-group-addon', true).first();
40683         
40684         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40685         
40686         var _this = this;
40687         
40688         (function(){
40689             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40690             _this.list.setWidth(lw);
40691         }).defer(100);
40692         
40693         this.list.on('mouseover', this.onViewOver, this);
40694         this.list.on('mousemove', this.onViewMove, this);
40695         this.list.on('scroll', this.onViewScroll, this);
40696         
40697         if(!this.tpl){
40698             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40699         }
40700         
40701         this.view = new Roo.View(this.list, this.tpl, {
40702             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40703         });
40704         
40705         this.view.on('click', this.onViewClick, this);
40706         
40707         this.store.on('beforeload', this.onBeforeLoad, this);
40708         this.store.on('load', this.onLoad, this);
40709         this.store.on('loadexception', this.onLoadException, this);
40710         
40711         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40712             "up" : function(e){
40713                 this.inKeyMode = true;
40714                 this.selectPrev();
40715             },
40716
40717             "down" : function(e){
40718                 if(!this.isExpanded()){
40719                     this.onTriggerClick();
40720                 }else{
40721                     this.inKeyMode = true;
40722                     this.selectNext();
40723                 }
40724             },
40725
40726             "enter" : function(e){
40727                 this.collapse();
40728                 
40729                 if(this.fireEvent("specialkey", this, e)){
40730                     this.onViewClick(false);
40731                 }
40732                 
40733                 return true;
40734             },
40735
40736             "esc" : function(e){
40737                 this.collapse();
40738             },
40739
40740             "tab" : function(e){
40741                 this.collapse();
40742                 
40743                 if(this.fireEvent("specialkey", this, e)){
40744                     this.onViewClick(false);
40745                 }
40746                 
40747                 return true;
40748             },
40749
40750             scope : this,
40751
40752             doRelay : function(foo, bar, hname){
40753                 if(hname == 'down' || this.scope.isExpanded()){
40754                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40755                 }
40756                 return true;
40757             },
40758
40759             forceKeyDown: true
40760         });
40761         
40762         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40763         
40764     },
40765     
40766     initNumberEvent : function(e)
40767     {
40768         this.inputEl().on("keydown" , this.fireKey,  this);
40769         this.inputEl().on("focus", this.onFocus,  this);
40770         this.inputEl().on("blur", this.onBlur,  this);
40771         
40772         this.inputEl().relayEvent('keyup', this);
40773         
40774         if(this.indicator){
40775             this.indicator.addClass('invisible');
40776         }
40777  
40778         this.originalValue = this.getValue();
40779         
40780         if(this.validationEvent == 'keyup'){
40781             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40782             this.inputEl().on('keyup', this.filterValidation, this);
40783         }
40784         else if(this.validationEvent !== false){
40785             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40786         }
40787         
40788         if(this.selectOnFocus){
40789             this.on("focus", this.preFocus, this);
40790             
40791         }
40792         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40793             this.inputEl().on("keypress", this.filterKeys, this);
40794         } else {
40795             this.inputEl().relayEvent('keypress', this);
40796         }
40797         
40798         var allowed = "0123456789";
40799         
40800         if(this.allowDecimals){
40801             allowed += this.decimalSeparator;
40802         }
40803         
40804         if(this.allowNegative){
40805             allowed += "-";
40806         }
40807         
40808         if(this.thousandsDelimiter) {
40809             allowed += ",";
40810         }
40811         
40812         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40813         
40814         var keyPress = function(e){
40815             
40816             var k = e.getKey();
40817             
40818             var c = e.getCharCode();
40819             
40820             if(
40821                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40822                     allowed.indexOf(String.fromCharCode(c)) === -1
40823             ){
40824                 e.stopEvent();
40825                 return;
40826             }
40827             
40828             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40829                 return;
40830             }
40831             
40832             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40833                 e.stopEvent();
40834             }
40835         };
40836         
40837         this.inputEl().on("keypress", keyPress, this);
40838         
40839     },
40840     
40841     onTriggerClick : function(e)
40842     {   
40843         if(this.disabled){
40844             return;
40845         }
40846         
40847         this.page = 0;
40848         this.loadNext = false;
40849         
40850         if(this.isExpanded()){
40851             this.collapse();
40852             return;
40853         }
40854         
40855         this.hasFocus = true;
40856         
40857         if(this.triggerAction == 'all') {
40858             this.doQuery(this.allQuery, true);
40859             return;
40860         }
40861         
40862         this.doQuery(this.getRawValue());
40863     },
40864     
40865     getCurrency : function()
40866     {   
40867         var v = this.currencyEl().getValue();
40868         
40869         return v;
40870     },
40871     
40872     restrictHeight : function()
40873     {
40874         this.list.alignTo(this.currencyEl(), this.listAlign);
40875         this.list.alignTo(this.currencyEl(), this.listAlign);
40876     },
40877     
40878     onViewClick : function(view, doFocus, el, e)
40879     {
40880         var index = this.view.getSelectedIndexes()[0];
40881         
40882         var r = this.store.getAt(index);
40883         
40884         if(r){
40885             this.onSelect(r, index);
40886         }
40887     },
40888     
40889     onSelect : function(record, index){
40890         
40891         if(this.fireEvent('beforeselect', this, record, index) !== false){
40892         
40893             this.setFromCurrencyData(index > -1 ? record.data : false);
40894             
40895             this.collapse();
40896             
40897             this.fireEvent('select', this, record, index);
40898         }
40899     },
40900     
40901     setFromCurrencyData : function(o)
40902     {
40903         var currency = '';
40904         
40905         this.lastCurrency = o;
40906         
40907         if (this.currencyField) {
40908             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40909         } else {
40910             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40911         }
40912         
40913         this.lastSelectionText = currency;
40914         
40915         //setting default currency
40916         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40917             this.setCurrency(this.defaultCurrency);
40918             return;
40919         }
40920         
40921         this.setCurrency(currency);
40922     },
40923     
40924     setFromData : function(o)
40925     {
40926         var c = {};
40927         
40928         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40929         
40930         this.setFromCurrencyData(c);
40931         
40932         var value = '';
40933         
40934         if (this.name) {
40935             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40936         } else {
40937             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40938         }
40939         
40940         this.setValue(value);
40941         
40942     },
40943     
40944     setCurrency : function(v)
40945     {   
40946         this.currencyValue = v;
40947         
40948         if(this.rendered){
40949             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40950             this.validate();
40951         }
40952     },
40953     
40954     setValue : function(v)
40955     {
40956         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40957         
40958         this.value = v;
40959         
40960         if(this.rendered){
40961             
40962             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40963             
40964             this.inputEl().dom.value = (v == '') ? '' :
40965                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40966             
40967             if(!this.allowZero && v === '0') {
40968                 this.hiddenEl().dom.value = '';
40969                 this.inputEl().dom.value = '';
40970             }
40971             
40972             this.validate();
40973         }
40974     },
40975     
40976     getRawValue : function()
40977     {
40978         var v = this.inputEl().getValue();
40979         
40980         return v;
40981     },
40982     
40983     getValue : function()
40984     {
40985         return this.fixPrecision(this.parseValue(this.getRawValue()));
40986     },
40987     
40988     parseValue : function(value)
40989     {
40990         if(this.thousandsDelimiter) {
40991             value += "";
40992             r = new RegExp(",", "g");
40993             value = value.replace(r, "");
40994         }
40995         
40996         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40997         return isNaN(value) ? '' : value;
40998         
40999     },
41000     
41001     fixPrecision : function(value)
41002     {
41003         if(this.thousandsDelimiter) {
41004             value += "";
41005             r = new RegExp(",", "g");
41006             value = value.replace(r, "");
41007         }
41008         
41009         var nan = isNaN(value);
41010         
41011         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41012             return nan ? '' : value;
41013         }
41014         return parseFloat(value).toFixed(this.decimalPrecision);
41015     },
41016     
41017     decimalPrecisionFcn : function(v)
41018     {
41019         return Math.floor(v);
41020     },
41021     
41022     validateValue : function(value)
41023     {
41024         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41025             return false;
41026         }
41027         
41028         var num = this.parseValue(value);
41029         
41030         if(isNaN(num)){
41031             this.markInvalid(String.format(this.nanText, value));
41032             return false;
41033         }
41034         
41035         if(num < this.minValue){
41036             this.markInvalid(String.format(this.minText, this.minValue));
41037             return false;
41038         }
41039         
41040         if(num > this.maxValue){
41041             this.markInvalid(String.format(this.maxText, this.maxValue));
41042             return false;
41043         }
41044         
41045         return true;
41046     },
41047     
41048     validate : function()
41049     {
41050         if(this.disabled || this.allowBlank){
41051             this.markValid();
41052             return true;
41053         }
41054         
41055         var currency = this.getCurrency();
41056         
41057         if(this.validateValue(this.getRawValue()) && currency.length){
41058             this.markValid();
41059             return true;
41060         }
41061         
41062         this.markInvalid();
41063         return false;
41064     },
41065     
41066     getName: function()
41067     {
41068         return this.name;
41069     },
41070     
41071     beforeBlur : function()
41072     {
41073         if(!this.castInt){
41074             return;
41075         }
41076         
41077         var v = this.parseValue(this.getRawValue());
41078         
41079         if(v || v == 0){
41080             this.setValue(v);
41081         }
41082     },
41083     
41084     onBlur : function()
41085     {
41086         this.beforeBlur();
41087         
41088         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41089             //this.el.removeClass(this.focusClass);
41090         }
41091         
41092         this.hasFocus = false;
41093         
41094         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41095             this.validate();
41096         }
41097         
41098         var v = this.getValue();
41099         
41100         if(String(v) !== String(this.startValue)){
41101             this.fireEvent('change', this, v, this.startValue);
41102         }
41103         
41104         this.fireEvent("blur", this);
41105     },
41106     
41107     inputEl : function()
41108     {
41109         return this.el.select('.roo-money-amount-input', true).first();
41110     },
41111     
41112     currencyEl : function()
41113     {
41114         return this.el.select('.roo-money-currency-input', true).first();
41115     },
41116     
41117     hiddenEl : function()
41118     {
41119         return this.el.select('input.hidden-number-input',true).first();
41120     }
41121     
41122 });