Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
22  
23  * @constructor
24  * Do not use directly - it does not do anything..
25  * @param {Object} config The config object
26  */
27
28
29
30 Roo.bootstrap.Component = function(config){
31     Roo.bootstrap.Component.superclass.constructor.call(this, config);
32        
33     this.addEvents({
34         /**
35          * @event childrenrendered
36          * Fires when the children have been rendered..
37          * @param {Roo.bootstrap.Component} this
38          */
39         "childrenrendered" : true
40         
41         
42         
43     });
44     
45     
46 };
47
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
49     
50     
51     allowDomMove : false, // to stop relocations in parent onRender...
52     
53     cls : false,
54     
55     style : false,
56     
57     autoCreate : false,
58     
59     tooltip : null,
60     /**
61      * Initialize Events for the element
62      */
63     initEvents : function() { },
64     
65     xattr : false,
66     
67     parentId : false,
68     
69     can_build_overlaid : true,
70     
71     container_method : false,
72     
73     dataId : false,
74     
75     name : false,
76     
77     parent: function() {
78         // returns the parent component..
79         return Roo.ComponentMgr.get(this.parentId)
80         
81         
82     },
83     
84     // private
85     onRender : function(ct, position)
86     {
87        // Roo.log("Call onRender: " + this.xtype);
88         
89         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
90         
91         if(this.el){
92             if (this.el.attr('xtype')) {
93                 this.el.attr('xtypex', this.el.attr('xtype'));
94                 this.el.dom.removeAttribute('xtype');
95                 
96                 this.initEvents();
97             }
98             
99             return;
100         }
101         
102          
103         
104         var cfg = Roo.apply({},  this.getAutoCreate());
105         
106         cfg.id = this.id || Roo.id();
107         
108         // fill in the extra attributes 
109         if (this.xattr && typeof(this.xattr) =='object') {
110             for (var i in this.xattr) {
111                 cfg[i] = this.xattr[i];
112             }
113         }
114         
115         if(this.dataId){
116             cfg.dataId = this.dataId;
117         }
118         
119         if (this.cls) {
120             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
121         }
122         
123         if (this.style) { // fixme needs to support more complex style data.
124             cfg.style = this.style;
125         }
126         
127         if(this.name){
128             cfg.name = this.name;
129         }
130         
131         this.el = ct.createChild(cfg, position);
132         
133         if (this.tooltip) {
134             this.tooltipEl().attr('tooltip', this.tooltip);
135         }
136         
137         if(this.tabIndex !== undefined){
138             this.el.dom.setAttribute('tabIndex', this.tabIndex);
139         }
140         
141         this.initEvents();
142         
143     },
144     /**
145      * Fetch the element to add children to
146      * @return {Roo.Element} defaults to this.el
147      */
148     getChildContainer : function()
149     {
150         return this.el;
151     },
152     /**
153      * Fetch the element to display the tooltip on.
154      * @return {Roo.Element} defaults to this.el
155      */
156     tooltipEl : function()
157     {
158         return this.el;
159     },
160         
161     addxtype  : function(tree,cntr)
162     {
163         var cn = this;
164         
165         cn = Roo.factory(tree);
166         //Roo.log(['addxtype', cn]);
167            
168         cn.parentType = this.xtype; //??
169         cn.parentId = this.id;
170         
171         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172         if (typeof(cn.container_method) == 'string') {
173             cntr = cn.container_method;
174         }
175         
176         
177         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
178         
179         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
180         
181         var build_from_html =  Roo.XComponent.build_from_html;
182           
183         var is_body  = (tree.xtype == 'Body') ;
184           
185         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
186           
187         var self_cntr_el = Roo.get(this[cntr](false));
188         
189         // do not try and build conditional elements 
190         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
191             return false;
192         }
193         
194         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196                 return this.addxtypeChild(tree,cntr, is_body);
197             }
198             
199             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
200                 
201             if(echild){
202                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
203             }
204             
205             Roo.log('skipping render');
206             return cn;
207             
208         }
209         
210         var ret = false;
211         if (!build_from_html) {
212             return false;
213         }
214         
215         // this i think handles overlaying multiple children of the same type
216         // with the sam eelement.. - which might be buggy..
217         while (true) {
218             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
219             
220             if (!echild) {
221                 break;
222             }
223             
224             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
225                 break;
226             }
227             
228             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
229         }
230        
231         return ret;
232     },
233     
234     
235     addxtypeChild : function (tree, cntr, is_body)
236     {
237         Roo.debug && Roo.log('addxtypeChild:' + cntr);
238         var cn = this;
239         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
240         
241         
242         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243                     (typeof(tree['flexy:foreach']) != 'undefined');
244           
245     
246         
247         skip_children = false;
248         // render the element if it's not BODY.
249         if (!is_body) {
250             
251             // if parent was disabled, then do not try and create the children..
252             if(!this[cntr](true)){
253                 tree.items = [];
254                 return tree;
255             }
256            
257             cn = Roo.factory(tree);
258            
259             cn.parentType = this.xtype; //??
260             cn.parentId = this.id;
261             
262             var build_from_html =  Roo.XComponent.build_from_html;
263             
264             
265             // does the container contain child eleemnts with 'xtype' attributes.
266             // that match this xtype..
267             // note - when we render we create these as well..
268             // so we should check to see if body has xtype set.
269             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
270                
271                 var self_cntr_el = Roo.get(this[cntr](false));
272                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
273                 if (echild) { 
274                     //Roo.log(Roo.XComponent.build_from_html);
275                     //Roo.log("got echild:");
276                     //Roo.log(echild);
277                 }
278                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279                 // and are not displayed -this causes this to use up the wrong element when matching.
280                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
281                 
282                 
283                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
285                   
286                   
287                   
288                     cn.el = echild;
289                   //  Roo.log("GOT");
290                     //echild.dom.removeAttribute('xtype');
291                 } else {
292                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293                     Roo.debug && Roo.log(self_cntr_el);
294                     Roo.debug && Roo.log(echild);
295                     Roo.debug && Roo.log(cn);
296                 }
297             }
298            
299             
300            
301             // if object has flexy:if - then it may or may not be rendered.
302             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
303                 // skip a flexy if element.
304                 Roo.debug && Roo.log('skipping render');
305                 Roo.debug && Roo.log(tree);
306                 if (!cn.el) {
307                     Roo.debug && Roo.log('skipping all children');
308                     skip_children = true;
309                 }
310                 
311              } else {
312                  
313                 // actually if flexy:foreach is found, we really want to create 
314                 // multiple copies here...
315                 //Roo.log('render');
316                 //Roo.log(this[cntr]());
317                 // some elements do not have render methods.. like the layouts...
318                 /*
319                 if(this[cntr](true) === false){
320                     cn.items = [];
321                     return cn;
322                 }
323                 */
324                 cn.render && cn.render(this[cntr](true));
325                 
326              }
327             // then add the element..
328         }
329          
330         // handle the kids..
331         
332         var nitems = [];
333         /*
334         if (typeof (tree.menu) != 'undefined') {
335             tree.menu.parentType = cn.xtype;
336             tree.menu.triggerEl = cn.el;
337             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
338             
339         }
340         */
341         if (!tree.items || !tree.items.length) {
342             cn.items = nitems;
343             //Roo.log(["no children", this]);
344             
345             return cn;
346         }
347          
348         var items = tree.items;
349         delete tree.items;
350         
351         //Roo.log(items.length);
352             // add the items..
353         if (!skip_children) {    
354             for(var i =0;i < items.length;i++) {
355               //  Roo.log(['add child', items[i]]);
356                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
357             }
358         }
359         
360         cn.items = nitems;
361         
362         //Roo.log("fire childrenrendered");
363         
364         cn.fireEvent('childrenrendered', this);
365         
366         return cn;
367     },
368     
369     /**
370      * Set the element that will be used to show or hide
371      */
372     setVisibilityEl : function(el)
373     {
374         this.visibilityEl = el;
375     },
376     
377      /**
378      * Get the element that will be used to show or hide
379      */
380     getVisibilityEl : function()
381     {
382         if (typeof(this.visibilityEl) == 'object') {
383             return this.visibilityEl;
384         }
385         
386         if (typeof(this.visibilityEl) == 'string') {
387             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
388         }
389         
390         return this.getEl();
391     },
392     
393     /**
394      * Show a component - removes 'hidden' class
395      */
396     show : function()
397     {
398         if(!this.getVisibilityEl()){
399             return;
400         }
401          
402         this.getVisibilityEl().removeClass('hidden');
403         
404         this.fireEvent('show', this);
405         
406         
407     },
408     /**
409      * Hide a component - adds 'hidden' class
410      */
411     hide: function()
412     {
413         if(!this.getVisibilityEl()){
414             return;
415         }
416         
417         this.getVisibilityEl().addClass('hidden');
418         
419         this.fireEvent('hide', this);
420         
421     }
422 });
423
424  /*
425  * - LGPL
426  *
427  * Body
428  *
429  */
430
431 /**
432  * @class Roo.bootstrap.Body
433  * @extends Roo.bootstrap.Component
434  * Bootstrap Body class
435  *
436  * @constructor
437  * Create a new body
438  * @param {Object} config The config object
439  */
440
441 Roo.bootstrap.Body = function(config){
442
443     config = config || {};
444
445     Roo.bootstrap.Body.superclass.constructor.call(this, config);
446     this.el = Roo.get(config.el ? config.el : document.body );
447     if (this.cls && this.cls.length) {
448         Roo.get(document.body).addClass(this.cls);
449     }
450 };
451
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
453
454     is_body : true,// just to make sure it's constructed?
455
456         autoCreate : {
457         cls: 'container'
458     },
459     onRender : function(ct, position)
460     {
461        /* Roo.log("Roo.bootstrap.Body - onRender");
462         if (this.cls && this.cls.length) {
463             Roo.get(document.body).addClass(this.cls);
464         }
465         // style??? xttr???
466         */
467     }
468
469
470
471
472 });
473 /*
474  * - LGPL
475  *
476  * button group
477  * 
478  */
479
480
481 /**
482  * @class Roo.bootstrap.ButtonGroup
483  * @extends Roo.bootstrap.Component
484  * Bootstrap ButtonGroup class
485  * @cfg {String} size lg | sm | xs (default empty normal)
486  * @cfg {String} align vertical | justified  (default none)
487  * @cfg {String} direction up | down (default down)
488  * @cfg {Boolean} toolbar false | true
489  * @cfg {Boolean} btn true | false
490  * 
491  * 
492  * @constructor
493  * Create a new Input
494  * @param {Object} config The config object
495  */
496
497 Roo.bootstrap.ButtonGroup = function(config){
498     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
499 };
500
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
502     
503     size: '',
504     align: '',
505     direction: '',
506     toolbar: false,
507     btn: true,
508
509     getAutoCreate : function(){
510         var cfg = {
511             cls: 'btn-group',
512             html : null
513         };
514         
515         cfg.html = this.html || cfg.html;
516         
517         if (this.toolbar) {
518             cfg = {
519                 cls: 'btn-toolbar',
520                 html: null
521             };
522             
523             return cfg;
524         }
525         
526         if (['vertical','justified'].indexOf(this.align)!==-1) {
527             cfg.cls = 'btn-group-' + this.align;
528             
529             if (this.align == 'justified') {
530                 console.log(this.items);
531             }
532         }
533         
534         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535             cfg.cls += ' btn-group-' + this.size;
536         }
537         
538         if (this.direction == 'up') {
539             cfg.cls += ' dropup' ;
540         }
541         
542         return cfg;
543     }
544    
545 });
546
547  /*
548  * - LGPL
549  *
550  * button
551  * 
552  */
553
554 /**
555  * @class Roo.bootstrap.Button
556  * @extends Roo.bootstrap.Component
557  * Bootstrap Button class
558  * @cfg {String} html The button content
559  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
560  * @cfg {String} size ( lg | sm | xs)
561  * @cfg {String} tag ( a | input | submit)
562  * @cfg {String} href empty or href
563  * @cfg {Boolean} disabled default false;
564  * @cfg {Boolean} isClose default false;
565  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566  * @cfg {String} badge text for badge
567  * @cfg {String} theme (default|glow)  
568  * @cfg {Boolean} inverse dark themed version
569  * @cfg {Boolean} toggle is it a slidy toggle button
570  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571  * @cfg {String} ontext text for on slidy toggle state
572  * @cfg {String} offtext text for off slidy toggle state
573  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
574  * @cfg {Boolean} removeClass remove the standard class..
575  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
576  * 
577  * @constructor
578  * Create a new button
579  * @param {Object} config The config object
580  */
581
582
583 Roo.bootstrap.Button = function(config){
584     Roo.bootstrap.Button.superclass.constructor.call(this, config);
585     this.weightClass = ["btn-default", 
586                        "btn-primary", 
587                        "btn-success", 
588                        "btn-info", 
589                        "btn-warning",
590                        "btn-danger",
591                        "btn-link"
592                       ],  
593     this.addEvents({
594         // raw events
595         /**
596          * @event click
597          * When a butotn is pressed
598          * @param {Roo.bootstrap.Button} btn
599          * @param {Roo.EventObject} e
600          */
601         "click" : true,
602          /**
603          * @event toggle
604          * After the button has been toggles
605          * @param {Roo.bootstrap.Button} btn
606          * @param {Roo.EventObject} e
607          * @param {boolean} pressed (also available as button.pressed)
608          */
609         "toggle" : true
610     });
611 };
612
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
614     html: false,
615     active: false,
616     weight: '',
617     size: '',
618     tag: 'button',
619     href: '',
620     disabled: false,
621     isClose: false,
622     glyphicon: '',
623     badge: '',
624     theme: 'default',
625     inverse: false,
626     
627     toggle: false,
628     ontext: 'ON',
629     offtext: 'OFF',
630     defaulton: true,
631     preventDefault: true,
632     removeClass: false,
633     name: false,
634     target: false,
635      
636     pressed : null,
637      
638     
639     getAutoCreate : function(){
640         
641         var cfg = {
642             tag : 'button',
643             cls : 'roo-button',
644             html: ''
645         };
646         
647         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
649             this.tag = 'button';
650         } else {
651             cfg.tag = this.tag;
652         }
653         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
654         
655         if (this.toggle == true) {
656             cfg={
657                 tag: 'div',
658                 cls: 'slider-frame roo-button',
659                 cn: [
660                     {
661                         tag: 'span',
662                         'data-on-text':'ON',
663                         'data-off-text':'OFF',
664                         cls: 'slider-button',
665                         html: this.offtext
666                     }
667                 ]
668             };
669             
670             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671                 cfg.cls += ' '+this.weight;
672             }
673             
674             return cfg;
675         }
676         
677         if (this.isClose) {
678             cfg.cls += ' close';
679             
680             cfg["aria-hidden"] = true;
681             
682             cfg.html = "&times;";
683             
684             return cfg;
685         }
686         
687          
688         if (this.theme==='default') {
689             cfg.cls = 'btn roo-button';
690             
691             //if (this.parentType != 'Navbar') {
692             this.weight = this.weight.length ?  this.weight : 'default';
693             //}
694             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
695                 
696                 cfg.cls += ' btn-' + this.weight;
697             }
698         } else if (this.theme==='glow') {
699             
700             cfg.tag = 'a';
701             cfg.cls = 'btn-glow roo-button';
702             
703             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
704                 
705                 cfg.cls += ' ' + this.weight;
706             }
707         }
708    
709         
710         if (this.inverse) {
711             this.cls += ' inverse';
712         }
713         
714         
715         if (this.active || this.pressed === true) {
716             cfg.cls += ' active';
717         }
718         
719         if (this.disabled) {
720             cfg.disabled = 'disabled';
721         }
722         
723         if (this.items) {
724             Roo.log('changing to ul' );
725             cfg.tag = 'ul';
726             this.glyphicon = 'caret';
727         }
728         
729         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
730          
731         //gsRoo.log(this.parentType);
732         if (this.parentType === 'Navbar' && !this.parent().bar) {
733             Roo.log('changing to li?');
734             
735             cfg.tag = 'li';
736             
737             cfg.cls = '';
738             cfg.cn =  [{
739                 tag : 'a',
740                 cls : 'roo-button',
741                 html : this.html,
742                 href : this.href || '#'
743             }];
744             if (this.menu) {
745                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
746                 cfg.cls += ' dropdown';
747             }   
748             
749             delete cfg.html;
750             
751         }
752         
753        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
754         
755         if (this.glyphicon) {
756             cfg.html = ' ' + cfg.html;
757             
758             cfg.cn = [
759                 {
760                     tag: 'span',
761                     cls: 'glyphicon glyphicon-' + this.glyphicon
762                 }
763             ];
764         }
765         
766         if (this.badge) {
767             cfg.html += ' ';
768             
769             cfg.tag = 'a';
770             
771 //            cfg.cls='btn roo-button';
772             
773             cfg.href=this.href;
774             
775             var value = cfg.html;
776             
777             if(this.glyphicon){
778                 value = {
779                             tag: 'span',
780                             cls: 'glyphicon glyphicon-' + this.glyphicon,
781                             html: this.html
782                         };
783                 
784             }
785             
786             cfg.cn = [
787                 value,
788                 {
789                     tag: 'span',
790                     cls: 'badge',
791                     html: this.badge
792                 }
793             ];
794             
795             cfg.html='';
796         }
797         
798         if (this.menu) {
799             cfg.cls += ' dropdown';
800             cfg.html = typeof(cfg.html) != 'undefined' ?
801                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
802         }
803         
804         if (cfg.tag !== 'a' && this.href !== '') {
805             throw "Tag must be a to set href.";
806         } else if (this.href.length > 0) {
807             cfg.href = this.href;
808         }
809         
810         if(this.removeClass){
811             cfg.cls = '';
812         }
813         
814         if(this.target){
815             cfg.target = this.target;
816         }
817         
818         return cfg;
819     },
820     initEvents: function() {
821        // Roo.log('init events?');
822 //        Roo.log(this.el.dom);
823         // add the menu...
824         
825         if (typeof (this.menu) != 'undefined') {
826             this.menu.parentType = this.xtype;
827             this.menu.triggerEl = this.el;
828             this.addxtype(Roo.apply({}, this.menu));
829         }
830
831
832        if (this.el.hasClass('roo-button')) {
833             this.el.on('click', this.onClick, this);
834        } else {
835             this.el.select('.roo-button').on('click', this.onClick, this);
836        }
837        
838        if(this.removeClass){
839            this.el.on('click', this.onClick, this);
840        }
841        
842        this.el.enableDisplayMode();
843         
844     },
845     onClick : function(e)
846     {
847         if (this.disabled) {
848             return;
849         }
850         
851         Roo.log('button on click ');
852         if(this.preventDefault){
853             e.preventDefault();
854         }
855         
856         if (this.pressed === true || this.pressed === false) {
857             this.toggleActive(e);
858         }
859         
860         
861         this.fireEvent('click', this, e);
862     },
863     
864     /**
865      * Enables this button
866      */
867     enable : function()
868     {
869         this.disabled = false;
870         this.el.removeClass('disabled');
871     },
872     
873     /**
874      * Disable this button
875      */
876     disable : function()
877     {
878         this.disabled = true;
879         this.el.addClass('disabled');
880     },
881      /**
882      * sets the active state on/off, 
883      * @param {Boolean} state (optional) Force a particular state
884      */
885     setActive : function(v) {
886         
887         this.el[v ? 'addClass' : 'removeClass']('active');
888         this.pressed = v;
889     },
890      /**
891      * toggles the current active state 
892      */
893     toggleActive : function(e)
894     {
895         this.setActive(!this.pressed);
896         this.fireEvent('toggle', this, e, !this.pressed);
897     },
898      /**
899      * get the current active state
900      * @return {boolean} true if it's active
901      */
902     isActive : function()
903     {
904         return this.el.hasClass('active');
905     },
906     /**
907      * set the text of the first selected button
908      */
909     setText : function(str)
910     {
911         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
912     },
913     /**
914      * get the text of the first selected button
915      */
916     getText : function()
917     {
918         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
919     },
920     
921     setWeight : function(str)
922     {
923         this.el.removeClass(this.weightClass);
924         this.el.addClass('btn-' + str);        
925     }
926     
927     
928 });
929
930  /*
931  * - LGPL
932  *
933  * column
934  * 
935  */
936
937 /**
938  * @class Roo.bootstrap.Column
939  * @extends Roo.bootstrap.Component
940  * Bootstrap Column class
941  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
949  *
950  * 
951  * @cfg {Boolean} hidden (true|false) hide the element
952  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953  * @cfg {String} fa (ban|check|...) font awesome icon
954  * @cfg {Number} fasize (1|2|....) font awsome size
955
956  * @cfg {String} icon (info-sign|check|...) glyphicon name
957
958  * @cfg {String} html content of column.
959  * 
960  * @constructor
961  * Create a new Column
962  * @param {Object} config The config object
963  */
964
965 Roo.bootstrap.Column = function(config){
966     Roo.bootstrap.Column.superclass.constructor.call(this, config);
967 };
968
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
970     
971     xs: false,
972     sm: false,
973     md: false,
974     lg: false,
975     xsoff: false,
976     smoff: false,
977     mdoff: false,
978     lgoff: false,
979     html: '',
980     offset: 0,
981     alert: false,
982     fa: false,
983     icon : false,
984     hidden : false,
985     fasize : 1,
986     
987     getAutoCreate : function(){
988         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
989         
990         cfg = {
991             tag: 'div',
992             cls: 'column'
993         };
994         
995         var settings=this;
996         ['xs','sm','md','lg'].map(function(size){
997             //Roo.log( size + ':' + settings[size]);
998             
999             if (settings[size+'off'] !== false) {
1000                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1001             }
1002             
1003             if (settings[size] === false) {
1004                 return;
1005             }
1006             
1007             if (!settings[size]) { // 0 = hidden
1008                 cfg.cls += ' hidden-' + size;
1009                 return;
1010             }
1011             cfg.cls += ' col-' + size + '-' + settings[size];
1012             
1013         });
1014         
1015         if (this.hidden) {
1016             cfg.cls += ' hidden';
1017         }
1018         
1019         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020             cfg.cls +=' alert alert-' + this.alert;
1021         }
1022         
1023         
1024         if (this.html.length) {
1025             cfg.html = this.html;
1026         }
1027         if (this.fa) {
1028             var fasize = '';
1029             if (this.fasize > 1) {
1030                 fasize = ' fa-' + this.fasize + 'x';
1031             }
1032             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1033             
1034             
1035         }
1036         if (this.icon) {
1037             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1038         }
1039         
1040         return cfg;
1041     }
1042    
1043 });
1044
1045  
1046
1047  /*
1048  * - LGPL
1049  *
1050  * page container.
1051  * 
1052  */
1053
1054
1055 /**
1056  * @class Roo.bootstrap.Container
1057  * @extends Roo.bootstrap.Component
1058  * Bootstrap Container class
1059  * @cfg {Boolean} jumbotron is it a jumbotron element
1060  * @cfg {String} html content of element
1061  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1063  * @cfg {String} header content of header (for panel)
1064  * @cfg {String} footer content of footer (for panel)
1065  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066  * @cfg {String} tag (header|aside|section) type of HTML tag.
1067  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068  * @cfg {String} fa font awesome icon
1069  * @cfg {String} icon (info-sign|check|...) glyphicon name
1070  * @cfg {Boolean} hidden (true|false) hide the element
1071  * @cfg {Boolean} expandable (true|false) default false
1072  * @cfg {Boolean} expanded (true|false) default true
1073  * @cfg {String} rheader contet on the right of header
1074  * @cfg {Boolean} clickable (true|false) default false
1075
1076  *     
1077  * @constructor
1078  * Create a new Container
1079  * @param {Object} config The config object
1080  */
1081
1082 Roo.bootstrap.Container = function(config){
1083     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1084     
1085     this.addEvents({
1086         // raw events
1087          /**
1088          * @event expand
1089          * After the panel has been expand
1090          * 
1091          * @param {Roo.bootstrap.Container} this
1092          */
1093         "expand" : true,
1094         /**
1095          * @event collapse
1096          * After the panel has been collapsed
1097          * 
1098          * @param {Roo.bootstrap.Container} this
1099          */
1100         "collapse" : true,
1101         /**
1102          * @event click
1103          * When a element is chick
1104          * @param {Roo.bootstrap.Container} this
1105          * @param {Roo.EventObject} e
1106          */
1107         "click" : true
1108     });
1109 };
1110
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1112     
1113     jumbotron : false,
1114     well: '',
1115     panel : '',
1116     header: '',
1117     footer : '',
1118     sticky: '',
1119     tag : false,
1120     alert : false,
1121     fa: false,
1122     icon : false,
1123     expandable : false,
1124     rheader : '',
1125     expanded : true,
1126     clickable: false,
1127   
1128      
1129     getChildContainer : function() {
1130         
1131         if(!this.el){
1132             return false;
1133         }
1134         
1135         if (this.panel.length) {
1136             return this.el.select('.panel-body',true).first();
1137         }
1138         
1139         return this.el;
1140     },
1141     
1142     
1143     getAutoCreate : function(){
1144         
1145         var cfg = {
1146             tag : this.tag || 'div',
1147             html : '',
1148             cls : ''
1149         };
1150         if (this.jumbotron) {
1151             cfg.cls = 'jumbotron';
1152         }
1153         
1154         
1155         
1156         // - this is applied by the parent..
1157         //if (this.cls) {
1158         //    cfg.cls = this.cls + '';
1159         //}
1160         
1161         if (this.sticky.length) {
1162             
1163             var bd = Roo.get(document.body);
1164             if (!bd.hasClass('bootstrap-sticky')) {
1165                 bd.addClass('bootstrap-sticky');
1166                 Roo.select('html',true).setStyle('height', '100%');
1167             }
1168              
1169             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1170         }
1171         
1172         
1173         if (this.well.length) {
1174             switch (this.well) {
1175                 case 'lg':
1176                 case 'sm':
1177                     cfg.cls +=' well well-' +this.well;
1178                     break;
1179                 default:
1180                     cfg.cls +=' well';
1181                     break;
1182             }
1183         }
1184         
1185         if (this.hidden) {
1186             cfg.cls += ' hidden';
1187         }
1188         
1189         
1190         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191             cfg.cls +=' alert alert-' + this.alert;
1192         }
1193         
1194         var body = cfg;
1195         
1196         if (this.panel.length) {
1197             cfg.cls += ' panel panel-' + this.panel;
1198             cfg.cn = [];
1199             if (this.header.length) {
1200                 
1201                 var h = [];
1202                 
1203                 if(this.expandable){
1204                     
1205                     cfg.cls = cfg.cls + ' expandable';
1206                     
1207                     h.push({
1208                         tag: 'i',
1209                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1210                     });
1211                     
1212                 }
1213                 
1214                 h.push(
1215                     {
1216                         tag: 'span',
1217                         cls : 'panel-title',
1218                         html : (this.expandable ? '&nbsp;' : '') + this.header
1219                     },
1220                     {
1221                         tag: 'span',
1222                         cls: 'panel-header-right',
1223                         html: this.rheader
1224                     }
1225                 );
1226                 
1227                 cfg.cn.push({
1228                     cls : 'panel-heading',
1229                     style : this.expandable ? 'cursor: pointer' : '',
1230                     cn : h
1231                 });
1232                 
1233             }
1234             
1235             body = false;
1236             cfg.cn.push({
1237                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1238                 html : this.html
1239             });
1240             
1241             
1242             if (this.footer.length) {
1243                 cfg.cn.push({
1244                     cls : 'panel-footer',
1245                     html : this.footer
1246                     
1247                 });
1248             }
1249             
1250         }
1251         
1252         if (body) {
1253             body.html = this.html || cfg.html;
1254             // prefix with the icons..
1255             if (this.fa) {
1256                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1257             }
1258             if (this.icon) {
1259                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1260             }
1261             
1262             
1263         }
1264         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265             cfg.cls =  'container';
1266         }
1267         
1268         return cfg;
1269     },
1270     
1271     initEvents: function() 
1272     {
1273         if(this.expandable){
1274             var headerEl = this.headerEl();
1275         
1276             if(headerEl){
1277                 headerEl.on('click', this.onToggleClick, this);
1278             }
1279         }
1280         
1281         if(this.clickable){
1282             this.el.on('click', this.onClick, this);
1283         }
1284         
1285     },
1286     
1287     onToggleClick : function()
1288     {
1289         var headerEl = this.headerEl();
1290         
1291         if(!headerEl){
1292             return;
1293         }
1294         
1295         if(this.expanded){
1296             this.collapse();
1297             return;
1298         }
1299         
1300         this.expand();
1301     },
1302     
1303     expand : function()
1304     {
1305         if(this.fireEvent('expand', this)) {
1306             
1307             this.expanded = true;
1308             
1309             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1310             
1311             this.el.select('.panel-body',true).first().removeClass('hide');
1312             
1313             var toggleEl = this.toggleEl();
1314
1315             if(!toggleEl){
1316                 return;
1317             }
1318
1319             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1320         }
1321         
1322     },
1323     
1324     collapse : function()
1325     {
1326         if(this.fireEvent('collapse', this)) {
1327             
1328             this.expanded = false;
1329             
1330             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331             this.el.select('.panel-body',true).first().addClass('hide');
1332         
1333             var toggleEl = this.toggleEl();
1334
1335             if(!toggleEl){
1336                 return;
1337             }
1338
1339             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1340         }
1341     },
1342     
1343     toggleEl : function()
1344     {
1345         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1346             return;
1347         }
1348         
1349         return this.el.select('.panel-heading .fa',true).first();
1350     },
1351     
1352     headerEl : function()
1353     {
1354         if(!this.el || !this.panel.length || !this.header.length){
1355             return;
1356         }
1357         
1358         return this.el.select('.panel-heading',true).first()
1359     },
1360     
1361     bodyEl : function()
1362     {
1363         if(!this.el || !this.panel.length){
1364             return;
1365         }
1366         
1367         return this.el.select('.panel-body',true).first()
1368     },
1369     
1370     titleEl : function()
1371     {
1372         if(!this.el || !this.panel.length || !this.header.length){
1373             return;
1374         }
1375         
1376         return this.el.select('.panel-title',true).first();
1377     },
1378     
1379     setTitle : function(v)
1380     {
1381         var titleEl = this.titleEl();
1382         
1383         if(!titleEl){
1384             return;
1385         }
1386         
1387         titleEl.dom.innerHTML = v;
1388     },
1389     
1390     getTitle : function()
1391     {
1392         
1393         var titleEl = this.titleEl();
1394         
1395         if(!titleEl){
1396             return '';
1397         }
1398         
1399         return titleEl.dom.innerHTML;
1400     },
1401     
1402     setRightTitle : function(v)
1403     {
1404         var t = this.el.select('.panel-header-right',true).first();
1405         
1406         if(!t){
1407             return;
1408         }
1409         
1410         t.dom.innerHTML = v;
1411     },
1412     
1413     onClick : function(e)
1414     {
1415         e.preventDefault();
1416         
1417         this.fireEvent('click', this, e);
1418     }
1419 });
1420
1421  /*
1422  * - LGPL
1423  *
1424  * image
1425  * 
1426  */
1427
1428
1429 /**
1430  * @class Roo.bootstrap.Img
1431  * @extends Roo.bootstrap.Component
1432  * Bootstrap Img class
1433  * @cfg {Boolean} imgResponsive false | true
1434  * @cfg {String} border rounded | circle | thumbnail
1435  * @cfg {String} src image source
1436  * @cfg {String} alt image alternative text
1437  * @cfg {String} href a tag href
1438  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439  * @cfg {String} xsUrl xs image source
1440  * @cfg {String} smUrl sm image source
1441  * @cfg {String} mdUrl md image source
1442  * @cfg {String} lgUrl lg image source
1443  * 
1444  * @constructor
1445  * Create a new Input
1446  * @param {Object} config The config object
1447  */
1448
1449 Roo.bootstrap.Img = function(config){
1450     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1451     
1452     this.addEvents({
1453         // img events
1454         /**
1455          * @event click
1456          * The img click event for the img.
1457          * @param {Roo.EventObject} e
1458          */
1459         "click" : true
1460     });
1461 };
1462
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1464     
1465     imgResponsive: true,
1466     border: '',
1467     src: 'about:blank',
1468     href: false,
1469     target: false,
1470     xsUrl: '',
1471     smUrl: '',
1472     mdUrl: '',
1473     lgUrl: '',
1474
1475     getAutoCreate : function()
1476     {   
1477         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478             return this.createSingleImg();
1479         }
1480         
1481         var cfg = {
1482             tag: 'div',
1483             cls: 'roo-image-responsive-group',
1484             cn: []
1485         };
1486         var _this = this;
1487         
1488         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1489             
1490             if(!_this[size + 'Url']){
1491                 return;
1492             }
1493             
1494             var img = {
1495                 tag: 'img',
1496                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497                 html: _this.html || cfg.html,
1498                 src: _this[size + 'Url']
1499             };
1500             
1501             img.cls += ' roo-image-responsive-' + size;
1502             
1503             var s = ['xs', 'sm', 'md', 'lg'];
1504             
1505             s.splice(s.indexOf(size), 1);
1506             
1507             Roo.each(s, function(ss){
1508                 img.cls += ' hidden-' + ss;
1509             });
1510             
1511             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512                 cfg.cls += ' img-' + _this.border;
1513             }
1514             
1515             if(_this.alt){
1516                 cfg.alt = _this.alt;
1517             }
1518             
1519             if(_this.href){
1520                 var a = {
1521                     tag: 'a',
1522                     href: _this.href,
1523                     cn: [
1524                         img
1525                     ]
1526                 };
1527
1528                 if(this.target){
1529                     a.target = _this.target;
1530                 }
1531             }
1532             
1533             cfg.cn.push((_this.href) ? a : img);
1534             
1535         });
1536         
1537         return cfg;
1538     },
1539     
1540     createSingleImg : function()
1541     {
1542         var cfg = {
1543             tag: 'img',
1544             cls: (this.imgResponsive) ? 'img-responsive' : '',
1545             html : null,
1546             src : 'about:blank'  // just incase src get's set to undefined?!?
1547         };
1548         
1549         cfg.html = this.html || cfg.html;
1550         
1551         cfg.src = this.src || cfg.src;
1552         
1553         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554             cfg.cls += ' img-' + this.border;
1555         }
1556         
1557         if(this.alt){
1558             cfg.alt = this.alt;
1559         }
1560         
1561         if(this.href){
1562             var a = {
1563                 tag: 'a',
1564                 href: this.href,
1565                 cn: [
1566                     cfg
1567                 ]
1568             };
1569             
1570             if(this.target){
1571                 a.target = this.target;
1572             }
1573             
1574         }
1575         
1576         return (this.href) ? a : cfg;
1577     },
1578     
1579     initEvents: function() 
1580     {
1581         if(!this.href){
1582             this.el.on('click', this.onClick, this);
1583         }
1584         
1585     },
1586     
1587     onClick : function(e)
1588     {
1589         Roo.log('img onclick');
1590         this.fireEvent('click', this, e);
1591     },
1592     /**
1593      * Sets the url of the image - used to update it
1594      * @param {String} url the url of the image
1595      */
1596     
1597     setSrc : function(url)
1598     {
1599         this.src =  url;
1600         
1601         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602             this.el.dom.src =  url;
1603             return;
1604         }
1605         
1606         this.el.select('img', true).first().dom.src =  url;
1607     }
1608     
1609     
1610    
1611 });
1612
1613  /*
1614  * - LGPL
1615  *
1616  * image
1617  * 
1618  */
1619
1620
1621 /**
1622  * @class Roo.bootstrap.Link
1623  * @extends Roo.bootstrap.Component
1624  * Bootstrap Link Class
1625  * @cfg {String} alt image alternative text
1626  * @cfg {String} href a tag href
1627  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628  * @cfg {String} html the content of the link.
1629  * @cfg {String} anchor name for the anchor link
1630  * @cfg {String} fa - favicon
1631
1632  * @cfg {Boolean} preventDefault (true | false) default false
1633
1634  * 
1635  * @constructor
1636  * Create a new Input
1637  * @param {Object} config The config object
1638  */
1639
1640 Roo.bootstrap.Link = function(config){
1641     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1642     
1643     this.addEvents({
1644         // img events
1645         /**
1646          * @event click
1647          * The img click event for the img.
1648          * @param {Roo.EventObject} e
1649          */
1650         "click" : true
1651     });
1652 };
1653
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1655     
1656     href: false,
1657     target: false,
1658     preventDefault: false,
1659     anchor : false,
1660     alt : false,
1661     fa: false,
1662
1663
1664     getAutoCreate : function()
1665     {
1666         var html = this.html || '';
1667         
1668         if (this.fa !== false) {
1669             html = '<i class="fa fa-' + this.fa + '"></i>';
1670         }
1671         var cfg = {
1672             tag: 'a'
1673         };
1674         // anchor's do not require html/href...
1675         if (this.anchor === false) {
1676             cfg.html = html;
1677             cfg.href = this.href || '#';
1678         } else {
1679             cfg.name = this.anchor;
1680             if (this.html !== false || this.fa !== false) {
1681                 cfg.html = html;
1682             }
1683             if (this.href !== false) {
1684                 cfg.href = this.href;
1685             }
1686         }
1687         
1688         if(this.alt !== false){
1689             cfg.alt = this.alt;
1690         }
1691         
1692         
1693         if(this.target !== false) {
1694             cfg.target = this.target;
1695         }
1696         
1697         return cfg;
1698     },
1699     
1700     initEvents: function() {
1701         
1702         if(!this.href || this.preventDefault){
1703             this.el.on('click', this.onClick, this);
1704         }
1705     },
1706     
1707     onClick : function(e)
1708     {
1709         if(this.preventDefault){
1710             e.preventDefault();
1711         }
1712         //Roo.log('img onclick');
1713         this.fireEvent('click', this, e);
1714     }
1715    
1716 });
1717
1718  /*
1719  * - LGPL
1720  *
1721  * header
1722  * 
1723  */
1724
1725 /**
1726  * @class Roo.bootstrap.Header
1727  * @extends Roo.bootstrap.Component
1728  * Bootstrap Header class
1729  * @cfg {String} html content of header
1730  * @cfg {Number} level (1|2|3|4|5|6) default 1
1731  * 
1732  * @constructor
1733  * Create a new Header
1734  * @param {Object} config The config object
1735  */
1736
1737
1738 Roo.bootstrap.Header  = function(config){
1739     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1740 };
1741
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1743     
1744     //href : false,
1745     html : false,
1746     level : 1,
1747     
1748     
1749     
1750     getAutoCreate : function(){
1751         
1752         
1753         
1754         var cfg = {
1755             tag: 'h' + (1 *this.level),
1756             html: this.html || ''
1757         } ;
1758         
1759         return cfg;
1760     }
1761    
1762 });
1763
1764  
1765
1766  /*
1767  * Based on:
1768  * Ext JS Library 1.1.1
1769  * Copyright(c) 2006-2007, Ext JS, LLC.
1770  *
1771  * Originally Released Under LGPL - original licence link has changed is not relivant.
1772  *
1773  * Fork - LGPL
1774  * <script type="text/javascript">
1775  */
1776  
1777 /**
1778  * @class Roo.bootstrap.MenuMgr
1779  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1780  * @singleton
1781  */
1782 Roo.bootstrap.MenuMgr = function(){
1783    var menus, active, groups = {}, attached = false, lastShow = new Date();
1784
1785    // private - called when first menu is created
1786    function init(){
1787        menus = {};
1788        active = new Roo.util.MixedCollection();
1789        Roo.get(document).addKeyListener(27, function(){
1790            if(active.length > 0){
1791                hideAll();
1792            }
1793        });
1794    }
1795
1796    // private
1797    function hideAll(){
1798        if(active && active.length > 0){
1799            var c = active.clone();
1800            c.each(function(m){
1801                m.hide();
1802            });
1803        }
1804    }
1805
1806    // private
1807    function onHide(m){
1808        active.remove(m);
1809        if(active.length < 1){
1810            Roo.get(document).un("mouseup", onMouseDown);
1811             
1812            attached = false;
1813        }
1814    }
1815
1816    // private
1817    function onShow(m){
1818        var last = active.last();
1819        lastShow = new Date();
1820        active.add(m);
1821        if(!attached){
1822           Roo.get(document).on("mouseup", onMouseDown);
1823            
1824            attached = true;
1825        }
1826        if(m.parentMenu){
1827           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828           m.parentMenu.activeChild = m;
1829        }else if(last && last.isVisible()){
1830           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1831        }
1832    }
1833
1834    // private
1835    function onBeforeHide(m){
1836        if(m.activeChild){
1837            m.activeChild.hide();
1838        }
1839        if(m.autoHideTimer){
1840            clearTimeout(m.autoHideTimer);
1841            delete m.autoHideTimer;
1842        }
1843    }
1844
1845    // private
1846    function onBeforeShow(m){
1847        var pm = m.parentMenu;
1848        if(!pm && !m.allowOtherMenus){
1849            hideAll();
1850        }else if(pm && pm.activeChild && active != m){
1851            pm.activeChild.hide();
1852        }
1853    }
1854
1855    // private this should really trigger on mouseup..
1856    function onMouseDown(e){
1857         Roo.log("on Mouse Up");
1858         
1859         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860             Roo.log("MenuManager hideAll");
1861             hideAll();
1862             e.stopEvent();
1863         }
1864         
1865         
1866    }
1867
1868    // private
1869    function onBeforeCheck(mi, state){
1870        if(state){
1871            var g = groups[mi.group];
1872            for(var i = 0, l = g.length; i < l; i++){
1873                if(g[i] != mi){
1874                    g[i].setChecked(false);
1875                }
1876            }
1877        }
1878    }
1879
1880    return {
1881
1882        /**
1883         * Hides all menus that are currently visible
1884         */
1885        hideAll : function(){
1886             hideAll();  
1887        },
1888
1889        // private
1890        register : function(menu){
1891            if(!menus){
1892                init();
1893            }
1894            menus[menu.id] = menu;
1895            menu.on("beforehide", onBeforeHide);
1896            menu.on("hide", onHide);
1897            menu.on("beforeshow", onBeforeShow);
1898            menu.on("show", onShow);
1899            var g = menu.group;
1900            if(g && menu.events["checkchange"]){
1901                if(!groups[g]){
1902                    groups[g] = [];
1903                }
1904                groups[g].push(menu);
1905                menu.on("checkchange", onCheck);
1906            }
1907        },
1908
1909         /**
1910          * Returns a {@link Roo.menu.Menu} object
1911          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912          * be used to generate and return a new Menu instance.
1913          */
1914        get : function(menu){
1915            if(typeof menu == "string"){ // menu id
1916                return menus[menu];
1917            }else if(menu.events){  // menu instance
1918                return menu;
1919            }
1920            /*else if(typeof menu.length == 'number'){ // array of menu items?
1921                return new Roo.bootstrap.Menu({items:menu});
1922            }else{ // otherwise, must be a config
1923                return new Roo.bootstrap.Menu(menu);
1924            }
1925            */
1926            return false;
1927        },
1928
1929        // private
1930        unregister : function(menu){
1931            delete menus[menu.id];
1932            menu.un("beforehide", onBeforeHide);
1933            menu.un("hide", onHide);
1934            menu.un("beforeshow", onBeforeShow);
1935            menu.un("show", onShow);
1936            var g = menu.group;
1937            if(g && menu.events["checkchange"]){
1938                groups[g].remove(menu);
1939                menu.un("checkchange", onCheck);
1940            }
1941        },
1942
1943        // private
1944        registerCheckable : function(menuItem){
1945            var g = menuItem.group;
1946            if(g){
1947                if(!groups[g]){
1948                    groups[g] = [];
1949                }
1950                groups[g].push(menuItem);
1951                menuItem.on("beforecheckchange", onBeforeCheck);
1952            }
1953        },
1954
1955        // private
1956        unregisterCheckable : function(menuItem){
1957            var g = menuItem.group;
1958            if(g){
1959                groups[g].remove(menuItem);
1960                menuItem.un("beforecheckchange", onBeforeCheck);
1961            }
1962        }
1963    };
1964 }();/*
1965  * - LGPL
1966  *
1967  * menu
1968  * 
1969  */
1970
1971 /**
1972  * @class Roo.bootstrap.Menu
1973  * @extends Roo.bootstrap.Component
1974  * Bootstrap Menu class - container for MenuItems
1975  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1977  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1978  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1979  * 
1980  * @constructor
1981  * Create a new Menu
1982  * @param {Object} config The config object
1983  */
1984
1985
1986 Roo.bootstrap.Menu = function(config){
1987     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988     if (this.registerMenu && this.type != 'treeview')  {
1989         Roo.bootstrap.MenuMgr.register(this);
1990     }
1991     this.addEvents({
1992         /**
1993          * @event beforeshow
1994          * Fires before this menu is displayed
1995          * @param {Roo.menu.Menu} this
1996          */
1997         beforeshow : true,
1998         /**
1999          * @event beforehide
2000          * Fires before this menu is hidden
2001          * @param {Roo.menu.Menu} this
2002          */
2003         beforehide : true,
2004         /**
2005          * @event show
2006          * Fires after this menu is displayed
2007          * @param {Roo.menu.Menu} this
2008          */
2009         show : true,
2010         /**
2011          * @event hide
2012          * Fires after this menu is hidden
2013          * @param {Roo.menu.Menu} this
2014          */
2015         hide : true,
2016         /**
2017          * @event click
2018          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019          * @param {Roo.menu.Menu} this
2020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021          * @param {Roo.EventObject} e
2022          */
2023         click : true,
2024         /**
2025          * @event mouseover
2026          * Fires when the mouse is hovering over this menu
2027          * @param {Roo.menu.Menu} this
2028          * @param {Roo.EventObject} e
2029          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2030          */
2031         mouseover : true,
2032         /**
2033          * @event mouseout
2034          * Fires when the mouse exits this menu
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.EventObject} e
2037          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2038          */
2039         mouseout : true,
2040         /**
2041          * @event itemclick
2042          * Fires when a menu item contained in this menu is clicked
2043          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044          * @param {Roo.EventObject} e
2045          */
2046         itemclick: true
2047     });
2048     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2049 };
2050
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2052     
2053    /// html : false,
2054     //align : '',
2055     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2056     type: false,
2057     /**
2058      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2059      */
2060     registerMenu : true,
2061     
2062     menuItems :false, // stores the menu items..
2063     
2064     hidden:true,
2065         
2066     parentMenu : false,
2067     
2068     stopEvent : true,
2069     
2070     isLink : false,
2071     
2072     getChildContainer : function() {
2073         return this.el;  
2074     },
2075     
2076     getAutoCreate : function(){
2077          
2078         //if (['right'].indexOf(this.align)!==-1) {
2079         //    cfg.cn[1].cls += ' pull-right'
2080         //}
2081         
2082         
2083         var cfg = {
2084             tag : 'ul',
2085             cls : 'dropdown-menu' ,
2086             style : 'z-index:1000'
2087             
2088         };
2089         
2090         if (this.type === 'submenu') {
2091             cfg.cls = 'submenu active';
2092         }
2093         if (this.type === 'treeview') {
2094             cfg.cls = 'treeview-menu';
2095         }
2096         
2097         return cfg;
2098     },
2099     initEvents : function() {
2100         
2101        // Roo.log("ADD event");
2102        // Roo.log(this.triggerEl.dom);
2103         
2104         this.triggerEl.on('click', this.onTriggerClick, this);
2105         
2106         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2107         
2108         this.triggerEl.addClass('dropdown-toggle');
2109         
2110         if (Roo.isTouch) {
2111             this.el.on('touchstart'  , this.onTouch, this);
2112         }
2113         this.el.on('click' , this.onClick, this);
2114
2115         this.el.on("mouseover", this.onMouseOver, this);
2116         this.el.on("mouseout", this.onMouseOut, this);
2117         
2118     },
2119     
2120     findTargetItem : function(e)
2121     {
2122         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2123         if(!t){
2124             return false;
2125         }
2126         //Roo.log(t);         Roo.log(t.id);
2127         if(t && t.id){
2128             //Roo.log(this.menuitems);
2129             return this.menuitems.get(t.id);
2130             
2131             //return this.items.get(t.menuItemId);
2132         }
2133         
2134         return false;
2135     },
2136     
2137     onTouch : function(e) 
2138     {
2139         Roo.log("menu.onTouch");
2140         //e.stopEvent(); this make the user popdown broken
2141         this.onClick(e);
2142     },
2143     
2144     onClick : function(e)
2145     {
2146         Roo.log("menu.onClick");
2147         
2148         var t = this.findTargetItem(e);
2149         if(!t || t.isContainer){
2150             return;
2151         }
2152         Roo.log(e);
2153         /*
2154         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2155             if(t == this.activeItem && t.shouldDeactivate(e)){
2156                 this.activeItem.deactivate();
2157                 delete this.activeItem;
2158                 return;
2159             }
2160             if(t.canActivate){
2161                 this.setActiveItem(t, true);
2162             }
2163             return;
2164             
2165             
2166         }
2167         */
2168        
2169         Roo.log('pass click event');
2170         
2171         t.onClick(e);
2172         
2173         this.fireEvent("click", this, t, e);
2174         
2175         var _this = this;
2176         
2177         if(!t.href.length || t.href == '#'){
2178             (function() { _this.hide(); }).defer(100);
2179         }
2180         
2181     },
2182     
2183     onMouseOver : function(e){
2184         var t  = this.findTargetItem(e);
2185         //Roo.log(t);
2186         //if(t){
2187         //    if(t.canActivate && !t.disabled){
2188         //        this.setActiveItem(t, true);
2189         //    }
2190         //}
2191         
2192         this.fireEvent("mouseover", this, e, t);
2193     },
2194     isVisible : function(){
2195         return !this.hidden;
2196     },
2197      onMouseOut : function(e){
2198         var t  = this.findTargetItem(e);
2199         
2200         //if(t ){
2201         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2202         //        this.activeItem.deactivate();
2203         //        delete this.activeItem;
2204         //    }
2205         //}
2206         this.fireEvent("mouseout", this, e, t);
2207     },
2208     
2209     
2210     /**
2211      * Displays this menu relative to another element
2212      * @param {String/HTMLElement/Roo.Element} element The element to align to
2213      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214      * the element (defaults to this.defaultAlign)
2215      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2216      */
2217     show : function(el, pos, parentMenu){
2218         this.parentMenu = parentMenu;
2219         if(!this.el){
2220             this.render();
2221         }
2222         this.fireEvent("beforeshow", this);
2223         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2224     },
2225      /**
2226      * Displays this menu at a specific xy position
2227      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2229      */
2230     showAt : function(xy, parentMenu, /* private: */_e){
2231         this.parentMenu = parentMenu;
2232         if(!this.el){
2233             this.render();
2234         }
2235         if(_e !== false){
2236             this.fireEvent("beforeshow", this);
2237             //xy = this.el.adjustForConstraints(xy);
2238         }
2239         
2240         //this.el.show();
2241         this.hideMenuItems();
2242         this.hidden = false;
2243         this.triggerEl.addClass('open');
2244         
2245         // reassign x when hitting right
2246         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2248         }
2249         
2250         // reassign y when hitting bottom
2251         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2253         }
2254         
2255         // but the list may align on trigger left or trigger top... should it be a properity?
2256         
2257         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2258             this.el.setXY(xy);
2259         }
2260         
2261         this.focus();
2262         this.fireEvent("show", this);
2263     },
2264     
2265     focus : function(){
2266         return;
2267         if(!this.hidden){
2268             this.doFocus.defer(50, this);
2269         }
2270     },
2271
2272     doFocus : function(){
2273         if(!this.hidden){
2274             this.focusEl.focus();
2275         }
2276     },
2277
2278     /**
2279      * Hides this menu and optionally all parent menus
2280      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2281      */
2282     hide : function(deep)
2283     {
2284         
2285         this.hideMenuItems();
2286         if(this.el && this.isVisible()){
2287             this.fireEvent("beforehide", this);
2288             if(this.activeItem){
2289                 this.activeItem.deactivate();
2290                 this.activeItem = null;
2291             }
2292             this.triggerEl.removeClass('open');;
2293             this.hidden = true;
2294             this.fireEvent("hide", this);
2295         }
2296         if(deep === true && this.parentMenu){
2297             this.parentMenu.hide(true);
2298         }
2299     },
2300     
2301     onTriggerClick : function(e)
2302     {
2303         Roo.log('trigger click');
2304         
2305         var target = e.getTarget();
2306         
2307         Roo.log(target.nodeName.toLowerCase());
2308         
2309         if(target.nodeName.toLowerCase() === 'i'){
2310             e.preventDefault();
2311         }
2312         
2313     },
2314     
2315     onTriggerPress  : function(e)
2316     {
2317         Roo.log('trigger press');
2318         //Roo.log(e.getTarget());
2319        // Roo.log(this.triggerEl.dom);
2320        
2321         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322         var pel = Roo.get(e.getTarget());
2323         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324             Roo.log('is treeview or dropdown?');
2325             return;
2326         }
2327         
2328         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2329             return;
2330         }
2331         
2332         if (this.isVisible()) {
2333             Roo.log('hide');
2334             this.hide();
2335         } else {
2336             Roo.log('show');
2337             this.show(this.triggerEl, false, false);
2338         }
2339         
2340         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2341             e.stopEvent();
2342         }
2343         
2344     },
2345        
2346     
2347     hideMenuItems : function()
2348     {
2349         Roo.log("hide Menu Items");
2350         if (!this.el) { 
2351             return;
2352         }
2353         //$(backdrop).remove()
2354         this.el.select('.open',true).each(function(aa) {
2355             
2356             aa.removeClass('open');
2357           //var parent = getParent($(this))
2358           //var relatedTarget = { relatedTarget: this }
2359           
2360            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361           //if (e.isDefaultPrevented()) return
2362            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2363         });
2364     },
2365     addxtypeChild : function (tree, cntr) {
2366         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2367           
2368         this.menuitems.add(comp);
2369         return comp;
2370
2371     },
2372     getEl : function()
2373     {
2374         Roo.log(this.el);
2375         return this.el;
2376     },
2377     
2378     clear : function()
2379     {
2380         this.getEl().dom.innerHTML = '';
2381         this.menuitems.clear();
2382     }
2383 });
2384
2385  
2386  /*
2387  * - LGPL
2388  *
2389  * menu item
2390  * 
2391  */
2392
2393
2394 /**
2395  * @class Roo.bootstrap.MenuItem
2396  * @extends Roo.bootstrap.Component
2397  * Bootstrap MenuItem class
2398  * @cfg {String} html the menu label
2399  * @cfg {String} href the link
2400  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2403  * @cfg {String} fa favicon to show on left of menu item.
2404  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2405  * 
2406  * 
2407  * @constructor
2408  * Create a new MenuItem
2409  * @param {Object} config The config object
2410  */
2411
2412
2413 Roo.bootstrap.MenuItem = function(config){
2414     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2415     this.addEvents({
2416         // raw events
2417         /**
2418          * @event click
2419          * The raw click event for the entire grid.
2420          * @param {Roo.bootstrap.MenuItem} this
2421          * @param {Roo.EventObject} e
2422          */
2423         "click" : true
2424     });
2425 };
2426
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2428     
2429     href : false,
2430     html : false,
2431     preventDefault: false,
2432     isContainer : false,
2433     active : false,
2434     fa: false,
2435     
2436     getAutoCreate : function(){
2437         
2438         if(this.isContainer){
2439             return {
2440                 tag: 'li',
2441                 cls: 'dropdown-menu-item'
2442             };
2443         }
2444         var ctag = {
2445             tag: 'span',
2446             html: 'Link'
2447         };
2448         
2449         var anc = {
2450             tag : 'a',
2451             href : '#',
2452             cn : [  ]
2453         };
2454         
2455         if (this.fa !== false) {
2456             anc.cn.push({
2457                 tag : 'i',
2458                 cls : 'fa fa-' + this.fa
2459             });
2460         }
2461         
2462         anc.cn.push(ctag);
2463         
2464         
2465         var cfg= {
2466             tag: 'li',
2467             cls: 'dropdown-menu-item',
2468             cn: [ anc ]
2469         };
2470         if (this.parent().type == 'treeview') {
2471             cfg.cls = 'treeview-menu';
2472         }
2473         if (this.active) {
2474             cfg.cls += ' active';
2475         }
2476         
2477         
2478         
2479         anc.href = this.href || cfg.cn[0].href ;
2480         ctag.html = this.html || cfg.cn[0].html ;
2481         return cfg;
2482     },
2483     
2484     initEvents: function()
2485     {
2486         if (this.parent().type == 'treeview') {
2487             this.el.select('a').on('click', this.onClick, this);
2488         }
2489         
2490         if (this.menu) {
2491             this.menu.parentType = this.xtype;
2492             this.menu.triggerEl = this.el;
2493             this.menu = this.addxtype(Roo.apply({}, this.menu));
2494         }
2495         
2496     },
2497     onClick : function(e)
2498     {
2499         Roo.log('item on click ');
2500         
2501         if(this.preventDefault){
2502             e.preventDefault();
2503         }
2504         //this.parent().hideMenuItems();
2505         
2506         this.fireEvent('click', this, e);
2507     },
2508     getEl : function()
2509     {
2510         return this.el;
2511     } 
2512 });
2513
2514  
2515
2516  /*
2517  * - LGPL
2518  *
2519  * menu separator
2520  * 
2521  */
2522
2523
2524 /**
2525  * @class Roo.bootstrap.MenuSeparator
2526  * @extends Roo.bootstrap.Component
2527  * Bootstrap MenuSeparator class
2528  * 
2529  * @constructor
2530  * Create a new MenuItem
2531  * @param {Object} config The config object
2532  */
2533
2534
2535 Roo.bootstrap.MenuSeparator = function(config){
2536     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2537 };
2538
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2540     
2541     getAutoCreate : function(){
2542         var cfg = {
2543             cls: 'divider',
2544             tag : 'li'
2545         };
2546         
2547         return cfg;
2548     }
2549    
2550 });
2551
2552  
2553
2554  
2555 /*
2556 * Licence: LGPL
2557 */
2558
2559 /**
2560  * @class Roo.bootstrap.Modal
2561  * @extends Roo.bootstrap.Component
2562  * Bootstrap Modal class
2563  * @cfg {String} title Title of dialog
2564  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2566  * @cfg {Boolean} specificTitle default false
2567  * @cfg {Array} buttons Array of buttons or standard button set..
2568  * @cfg {String} buttonPosition (left|right|center) default right
2569  * @cfg {Boolean} animate default true
2570  * @cfg {Boolean} allow_close default true
2571  * @cfg {Boolean} fitwindow default false
2572  * @cfg {String} size (sm|lg) default empty
2573  * @cfg {Number} max_width set the max width of modal
2574  *
2575  *
2576  * @constructor
2577  * Create a new Modal Dialog
2578  * @param {Object} config The config object
2579  */
2580
2581 Roo.bootstrap.Modal = function(config){
2582     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2583     this.addEvents({
2584         // raw events
2585         /**
2586          * @event btnclick
2587          * The raw btnclick event for the button
2588          * @param {Roo.EventObject} e
2589          */
2590         "btnclick" : true,
2591         /**
2592          * @event resize
2593          * Fire when dialog resize
2594          * @param {Roo.bootstrap.Modal} this
2595          * @param {Roo.EventObject} e
2596          */
2597         "resize" : true
2598     });
2599     this.buttons = this.buttons || [];
2600
2601     if (this.tmpl) {
2602         this.tmpl = Roo.factory(this.tmpl);
2603     }
2604
2605 };
2606
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2608
2609     title : 'test dialog',
2610
2611     buttons : false,
2612
2613     // set on load...
2614
2615     html: false,
2616
2617     tmp: false,
2618
2619     specificTitle: false,
2620
2621     buttonPosition: 'right',
2622
2623     allow_close : true,
2624
2625     animate : true,
2626
2627     fitwindow: false,
2628     
2629      // private
2630     dialogEl: false,
2631     bodyEl:  false,
2632     footerEl:  false,
2633     titleEl:  false,
2634     closeEl:  false,
2635
2636     size: '',
2637     
2638     max_width: 0,
2639     
2640     max_height: 0,
2641     
2642     fit_content: false,
2643
2644     onRender : function(ct, position)
2645     {
2646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2647
2648         if(!this.el){
2649             var cfg = Roo.apply({},  this.getAutoCreate());
2650             cfg.id = Roo.id();
2651             //if(!cfg.name){
2652             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2653             //}
2654             //if (!cfg.name.length) {
2655             //    delete cfg.name;
2656            // }
2657             if (this.cls) {
2658                 cfg.cls += ' ' + this.cls;
2659             }
2660             if (this.style) {
2661                 cfg.style = this.style;
2662             }
2663             this.el = Roo.get(document.body).createChild(cfg, position);
2664         }
2665         //var type = this.el.dom.type;
2666
2667
2668         if(this.tabIndex !== undefined){
2669             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2670         }
2671
2672         this.dialogEl = this.el.select('.modal-dialog',true).first();
2673         this.bodyEl = this.el.select('.modal-body',true).first();
2674         this.closeEl = this.el.select('.modal-header .close', true).first();
2675         this.headerEl = this.el.select('.modal-header',true).first();
2676         this.titleEl = this.el.select('.modal-title',true).first();
2677         this.footerEl = this.el.select('.modal-footer',true).first();
2678
2679         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2680         
2681         //this.el.addClass("x-dlg-modal");
2682
2683         if (this.buttons.length) {
2684             Roo.each(this.buttons, function(bb) {
2685                 var b = Roo.apply({}, bb);
2686                 b.xns = b.xns || Roo.bootstrap;
2687                 b.xtype = b.xtype || 'Button';
2688                 if (typeof(b.listeners) == 'undefined') {
2689                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2690                 }
2691
2692                 var btn = Roo.factory(b);
2693
2694                 btn.render(this.el.select('.modal-footer div').first());
2695
2696             },this);
2697         }
2698         // render the children.
2699         var nitems = [];
2700
2701         if(typeof(this.items) != 'undefined'){
2702             var items = this.items;
2703             delete this.items;
2704
2705             for(var i =0;i < items.length;i++) {
2706                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2707             }
2708         }
2709
2710         this.items = nitems;
2711
2712         // where are these used - they used to be body/close/footer
2713
2714
2715         this.initEvents();
2716         //this.el.addClass([this.fieldClass, this.cls]);
2717
2718     },
2719
2720     getAutoCreate : function()
2721     {
2722         var bdy = {
2723                 cls : 'modal-body',
2724                 html : this.html || ''
2725         };
2726
2727         var title = {
2728             tag: 'h4',
2729             cls : 'modal-title',
2730             html : this.title
2731         };
2732
2733         if(this.specificTitle){
2734             title = this.title;
2735
2736         };
2737
2738         var header = [];
2739         if (this.allow_close) {
2740             header.push({
2741                 tag: 'button',
2742                 cls : 'close',
2743                 html : '&times'
2744             });
2745         }
2746
2747         header.push(title);
2748
2749         var size = '';
2750
2751         if(this.size.length){
2752             size = 'modal-' + this.size;
2753         }
2754
2755         var modal = {
2756             cls: "modal",
2757              cn : [
2758                 {
2759                     cls: "modal-dialog " + size,
2760                     cn : [
2761                         {
2762                             cls : "modal-content",
2763                             cn : [
2764                                 {
2765                                     cls : 'modal-header',
2766                                     cn : header
2767                                 },
2768                                 bdy,
2769                                 {
2770                                     cls : 'modal-footer',
2771                                     cn : [
2772                                         {
2773                                             tag: 'div',
2774                                             cls: 'btn-' + this.buttonPosition
2775                                         }
2776                                     ]
2777
2778                                 }
2779
2780
2781                             ]
2782
2783                         }
2784                     ]
2785
2786                 }
2787             ]
2788         };
2789
2790         if(this.animate){
2791             modal.cls += ' fade';
2792         }
2793
2794         return modal;
2795
2796     },
2797     getChildContainer : function() {
2798
2799          return this.bodyEl;
2800
2801     },
2802     getButtonContainer : function() {
2803          return this.el.select('.modal-footer div',true).first();
2804
2805     },
2806     initEvents : function()
2807     {
2808         if (this.allow_close) {
2809             this.closeEl.on('click', this.hide, this);
2810         }
2811         Roo.EventManager.onWindowResize(this.resize, this, true);
2812
2813
2814     },
2815
2816     resize : function()
2817     {
2818         this.maskEl.setSize(
2819             Roo.lib.Dom.getViewWidth(true),
2820             Roo.lib.Dom.getViewHeight(true)
2821         );
2822         
2823         if (this.fitwindow) {
2824             this.setSize(
2825                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2827             );
2828             return;
2829         }
2830         
2831         if(this.max_width !== 0) {
2832             
2833             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2834             
2835             if(this.height) {
2836                 this.setSize(w, this.height);
2837                 return;
2838             }
2839             
2840             if(this.max_height) {
2841                 this.setSize(w,Math.min(
2842                     this.max_height,
2843                     Roo.lib.Dom.getViewportHeight(true) - 60
2844                 ));
2845                 
2846                 return;
2847             }
2848             
2849             if(!this.fit_content) {
2850                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2851                 return;
2852             }
2853             
2854             this.setSize(w, Math.min(
2855                 60 +
2856                 this.headerEl.getHeight() + 
2857                 this.footerEl.getHeight() + 
2858                 this.getChildHeight(this.bodyEl.dom.childNodes),
2859                 Roo.lib.Dom.getViewportHeight(true) - 60)
2860             );
2861         }
2862         
2863     },
2864
2865     setSize : function(w,h)
2866     {
2867         if (!w && !h) {
2868             return;
2869         }
2870         
2871         this.resizeTo(w,h);
2872     },
2873
2874     show : function() {
2875
2876         if (!this.rendered) {
2877             this.render();
2878         }
2879
2880         //this.el.setStyle('display', 'block');
2881         this.el.removeClass('hideing');        
2882         this.el.addClass('show');
2883  
2884         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2885             var _this = this;
2886             (function(){
2887                 this.el.addClass('in');
2888             }).defer(50, this);
2889         }else{
2890             this.el.addClass('in');
2891         }
2892
2893         // not sure how we can show data in here..
2894         //if (this.tmpl) {
2895         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896         //}
2897
2898         Roo.get(document.body).addClass("x-body-masked");
2899         
2900         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2901         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902         this.maskEl.addClass('show');
2903         
2904         this.resize();
2905         
2906         this.fireEvent('show', this);
2907
2908         // set zindex here - otherwise it appears to be ignored...
2909         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910
2911         (function () {
2912             this.items.forEach( function(e) {
2913                 e.layout ? e.layout() : false;
2914
2915             });
2916         }).defer(100,this);
2917
2918     },
2919     hide : function()
2920     {
2921         if(this.fireEvent("beforehide", this) !== false){
2922             this.maskEl.removeClass('show');
2923             Roo.get(document.body).removeClass("x-body-masked");
2924             this.el.removeClass('in');
2925             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2926
2927             if(this.animate){ // why
2928                 this.el.addClass('hideing');
2929                 (function(){
2930                     if (!this.el.hasClass('hideing')) {
2931                         return; // it's been shown again...
2932                     }
2933                     this.el.removeClass('show');
2934                     this.el.removeClass('hideing');
2935                 }).defer(150,this);
2936                 
2937             }else{
2938                  this.el.removeClass('show');
2939             }
2940             this.fireEvent('hide', this);
2941         }
2942     },
2943     isVisible : function()
2944     {
2945         
2946         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2947         
2948     },
2949
2950     addButton : function(str, cb)
2951     {
2952
2953
2954         var b = Roo.apply({}, { html : str } );
2955         b.xns = b.xns || Roo.bootstrap;
2956         b.xtype = b.xtype || 'Button';
2957         if (typeof(b.listeners) == 'undefined') {
2958             b.listeners = { click : cb.createDelegate(this)  };
2959         }
2960
2961         var btn = Roo.factory(b);
2962
2963         btn.render(this.el.select('.modal-footer div').first());
2964
2965         return btn;
2966
2967     },
2968
2969     setDefaultButton : function(btn)
2970     {
2971         //this.el.select('.modal-footer').()
2972     },
2973     diff : false,
2974
2975     resizeTo: function(w,h)
2976     {
2977         // skip.. ?? why??
2978
2979         this.dialogEl.setWidth(w);
2980         if (this.diff === false) {
2981             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982         }
2983
2984         this.bodyEl.setHeight(h - this.diff);
2985
2986         this.fireEvent('resize', this);
2987
2988     },
2989     setContentSize  : function(w, h)
2990     {
2991
2992     },
2993     onButtonClick: function(btn,e)
2994     {
2995         //Roo.log([a,b,c]);
2996         this.fireEvent('btnclick', btn.name, e);
2997     },
2998      /**
2999      * Set the title of the Dialog
3000      * @param {String} str new Title
3001      */
3002     setTitle: function(str) {
3003         this.titleEl.dom.innerHTML = str;
3004     },
3005     /**
3006      * Set the body of the Dialog
3007      * @param {String} str new Title
3008      */
3009     setBody: function(str) {
3010         this.bodyEl.dom.innerHTML = str;
3011     },
3012     /**
3013      * Set the body of the Dialog using the template
3014      * @param {Obj} data - apply this data to the template and replace the body contents.
3015      */
3016     applyBody: function(obj)
3017     {
3018         if (!this.tmpl) {
3019             Roo.log("Error - using apply Body without a template");
3020             //code
3021         }
3022         this.tmpl.overwrite(this.bodyEl, obj);
3023     },
3024     
3025     getChildHeight : function(child_nodes)
3026     {
3027         if(
3028             !child_nodes ||
3029             child_nodes.length == 0
3030         ) {
3031             return;
3032         }
3033         
3034         var child_height = 0;
3035         
3036         for(var i = 0; i < child_nodes.length; i++) {
3037             
3038             /*
3039             * for modal with tabs...
3040             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3041                 
3042                 var layout_childs = child_nodes[i].childNodes;
3043                 
3044                 for(var j = 0; j < layout_childs.length; j++) {
3045                     
3046                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3047                         
3048                         var layout_body_childs = layout_childs[j].childNodes;
3049                         
3050                         for(var k = 0; k < layout_body_childs.length; k++) {
3051                             
3052                             if(layout_body_childs[k].classList.contains('navbar')) {
3053                                 child_height += layout_body_childs[k].offsetHeight;
3054                                 continue;
3055                             }
3056                             
3057                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3058                                 
3059                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3060                                 
3061                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3062                                     
3063                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3065                                         continue;
3066                                     }
3067                                     
3068                                 }
3069                                 
3070                             }
3071                             
3072                         }
3073                     }
3074                 }
3075                 continue;
3076             }
3077             */
3078             
3079             child_height += child_nodes[i].offsetHeight;
3080             // Roo.log(child_nodes[i].offsetHeight);
3081         }
3082         
3083         return child_height;
3084     }
3085
3086 });
3087
3088
3089 Roo.apply(Roo.bootstrap.Modal,  {
3090     /**
3091          * Button config that displays a single OK button
3092          * @type Object
3093          */
3094         OK :  [{
3095             name : 'ok',
3096             weight : 'primary',
3097             html : 'OK'
3098         }],
3099         /**
3100          * Button config that displays Yes and No buttons
3101          * @type Object
3102          */
3103         YESNO : [
3104             {
3105                 name  : 'no',
3106                 html : 'No'
3107             },
3108             {
3109                 name  :'yes',
3110                 weight : 'primary',
3111                 html : 'Yes'
3112             }
3113         ],
3114
3115         /**
3116          * Button config that displays OK and Cancel buttons
3117          * @type Object
3118          */
3119         OKCANCEL : [
3120             {
3121                name : 'cancel',
3122                 html : 'Cancel'
3123             },
3124             {
3125                 name : 'ok',
3126                 weight : 'primary',
3127                 html : 'OK'
3128             }
3129         ],
3130         /**
3131          * Button config that displays Yes, No and Cancel buttons
3132          * @type Object
3133          */
3134         YESNOCANCEL : [
3135             {
3136                 name : 'yes',
3137                 weight : 'primary',
3138                 html : 'Yes'
3139             },
3140             {
3141                 name : 'no',
3142                 html : 'No'
3143             },
3144             {
3145                 name : 'cancel',
3146                 html : 'Cancel'
3147             }
3148         ],
3149         
3150         zIndex : 10001
3151 });
3152 /*
3153  * - LGPL
3154  *
3155  * messagebox - can be used as a replace
3156  * 
3157  */
3158 /**
3159  * @class Roo.MessageBox
3160  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3161  * Example usage:
3162  *<pre><code>
3163 // Basic alert:
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3165
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3168     if (btn == 'ok'){
3169         // process text value...
3170     }
3171 });
3172
3173 // Show a dialog using config options:
3174 Roo.Msg.show({
3175    title:'Save Changes?',
3176    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177    buttons: Roo.Msg.YESNOCANCEL,
3178    fn: processResult,
3179    animEl: 'elId'
3180 });
3181 </code></pre>
3182  * @singleton
3183  */
3184 Roo.bootstrap.MessageBox = function(){
3185     var dlg, opt, mask, waitTimer;
3186     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187     var buttons, activeTextEl, bwidth;
3188
3189     
3190     // private
3191     var handleButton = function(button){
3192         dlg.hide();
3193         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3194     };
3195
3196     // private
3197     var handleHide = function(){
3198         if(opt && opt.cls){
3199             dlg.el.removeClass(opt.cls);
3200         }
3201         //if(waitTimer){
3202         //    Roo.TaskMgr.stop(waitTimer);
3203         //    waitTimer = null;
3204         //}
3205     };
3206
3207     // private
3208     var updateButtons = function(b){
3209         var width = 0;
3210         if(!b){
3211             buttons["ok"].hide();
3212             buttons["cancel"].hide();
3213             buttons["yes"].hide();
3214             buttons["no"].hide();
3215             //dlg.footer.dom.style.display = 'none';
3216             return width;
3217         }
3218         dlg.footerEl.dom.style.display = '';
3219         for(var k in buttons){
3220             if(typeof buttons[k] != "function"){
3221                 if(b[k]){
3222                     buttons[k].show();
3223                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224                     width += buttons[k].el.getWidth()+15;
3225                 }else{
3226                     buttons[k].hide();
3227                 }
3228             }
3229         }
3230         return width;
3231     };
3232
3233     // private
3234     var handleEsc = function(d, k, e){
3235         if(opt && opt.closable !== false){
3236             dlg.hide();
3237         }
3238         if(e){
3239             e.stopEvent();
3240         }
3241     };
3242
3243     return {
3244         /**
3245          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246          * @return {Roo.BasicDialog} The BasicDialog element
3247          */
3248         getDialog : function(){
3249            if(!dlg){
3250                 dlg = new Roo.bootstrap.Modal( {
3251                     //draggable: true,
3252                     //resizable:false,
3253                     //constraintoviewport:false,
3254                     //fixedcenter:true,
3255                     //collapsible : false,
3256                     //shim:true,
3257                     //modal: true,
3258                 //    width: 'auto',
3259                   //  height:100,
3260                     //buttonAlign:"center",
3261                     closeClick : function(){
3262                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3263                             handleButton("no");
3264                         }else{
3265                             handleButton("cancel");
3266                         }
3267                     }
3268                 });
3269                 dlg.render();
3270                 dlg.on("hide", handleHide);
3271                 mask = dlg.mask;
3272                 //dlg.addKeyListener(27, handleEsc);
3273                 buttons = {};
3274                 this.buttons = buttons;
3275                 var bt = this.buttonText;
3276                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3280                 //Roo.log(buttons);
3281                 bodyEl = dlg.bodyEl.createChild({
3282
3283                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284                         '<textarea class="roo-mb-textarea"></textarea>' +
3285                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3286                 });
3287                 msgEl = bodyEl.dom.firstChild;
3288                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289                 textboxEl.enableDisplayMode();
3290                 textboxEl.addKeyListener([10,13], function(){
3291                     if(dlg.isVisible() && opt && opt.buttons){
3292                         if(opt.buttons.ok){
3293                             handleButton("ok");
3294                         }else if(opt.buttons.yes){
3295                             handleButton("yes");
3296                         }
3297                     }
3298                 });
3299                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300                 textareaEl.enableDisplayMode();
3301                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302                 progressEl.enableDisplayMode();
3303                 
3304                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305                 var pf = progressEl.dom.firstChild;
3306                 if (pf) {
3307                     pp = Roo.get(pf.firstChild);
3308                     pp.setHeight(pf.offsetHeight);
3309                 }
3310                 
3311             }
3312             return dlg;
3313         },
3314
3315         /**
3316          * Updates the message box body text
3317          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318          * the XHTML-compliant non-breaking space character '&amp;#160;')
3319          * @return {Roo.MessageBox} This message box
3320          */
3321         updateText : function(text)
3322         {
3323             if(!dlg.isVisible() && !opt.width){
3324                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3326             }
3327             msgEl.innerHTML = text || '&#160;';
3328       
3329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3331             var w = Math.max(
3332                     Math.min(opt.width || cw , this.maxWidth), 
3333                     Math.max(opt.minWidth || this.minWidth, bwidth)
3334             );
3335             if(opt.prompt){
3336                 activeTextEl.setWidth(w);
3337             }
3338             if(dlg.isVisible()){
3339                 dlg.fixedcenter = false;
3340             }
3341             // to big, make it scroll. = But as usual stupid IE does not support
3342             // !important..
3343             
3344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3347             } else {
3348                 bodyEl.dom.style.height = '';
3349                 bodyEl.dom.style.overflowY = '';
3350             }
3351             if (cw > w) {
3352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3353             } else {
3354                 bodyEl.dom.style.overflowX = '';
3355             }
3356             
3357             dlg.setContentSize(w, bodyEl.getHeight());
3358             if(dlg.isVisible()){
3359                 dlg.fixedcenter = true;
3360             }
3361             return this;
3362         },
3363
3364         /**
3365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369          * @return {Roo.MessageBox} This message box
3370          */
3371         updateProgress : function(value, text){
3372             if(text){
3373                 this.updateText(text);
3374             }
3375             
3376             if (pp) { // weird bug on my firefox - for some reason this is not defined
3377                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3379             }
3380             return this;
3381         },        
3382
3383         /**
3384          * Returns true if the message box is currently displayed
3385          * @return {Boolean} True if the message box is visible, else false
3386          */
3387         isVisible : function(){
3388             return dlg && dlg.isVisible();  
3389         },
3390
3391         /**
3392          * Hides the message box if it is displayed
3393          */
3394         hide : function(){
3395             if(this.isVisible()){
3396                 dlg.hide();
3397             }  
3398         },
3399
3400         /**
3401          * Displays a new message box, or reinitializes an existing message box, based on the config options
3402          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403          * The following config object properties are supported:
3404          * <pre>
3405 Property    Type             Description
3406 ----------  ---------------  ------------------------------------------------------------------------------------
3407 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3408                                    closes (defaults to undefined)
3409 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3412                                    progress and wait dialogs will ignore this property and always hide the
3413                                    close button as they can only be closed programmatically.
3414 cls               String           A custom CSS class to apply to the message box element
3415 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3416                                    displayed (defaults to 75)
3417 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3418                                    function will be btn (the name of the button that was clicked, if applicable,
3419                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3420                                    Progress and wait dialogs will ignore this option since they do not respond to
3421                                    user actions and can only be closed programmatically, so any required function
3422                                    should be called by the same code after it closes the dialog.
3423 icon              String           A CSS class that provides a background image to be used as an icon for
3424                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3426 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3427 modal             Boolean          False to allow user interaction with the page while the message box is
3428                                    displayed (defaults to true)
3429 msg               String           A string that will replace the existing message box body text (defaults
3430                                    to the XHTML-compliant non-breaking space character '&#160;')
3431 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3432 progress          Boolean          True to display a progress bar (defaults to false)
3433 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3436 title             String           The title text
3437 value             String           The string value to set into the active textbox element if displayed
3438 wait              Boolean          True to display a progress bar (defaults to false)
3439 width             Number           The width of the dialog in pixels
3440 </pre>
3441          *
3442          * Example usage:
3443          * <pre><code>
3444 Roo.Msg.show({
3445    title: 'Address',
3446    msg: 'Please enter your address:',
3447    width: 300,
3448    buttons: Roo.MessageBox.OKCANCEL,
3449    multiline: true,
3450    fn: saveAddress,
3451    animEl: 'addAddressBtn'
3452 });
3453 </code></pre>
3454          * @param {Object} config Configuration options
3455          * @return {Roo.MessageBox} This message box
3456          */
3457         show : function(options)
3458         {
3459             
3460             // this causes nightmares if you show one dialog after another
3461             // especially on callbacks..
3462              
3463             if(this.isVisible()){
3464                 
3465                 this.hide();
3466                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3468                 Roo.log("New Dialog Message:" +  options.msg )
3469                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3471                 
3472             }
3473             var d = this.getDialog();
3474             opt = options;
3475             d.setTitle(opt.title || "&#160;");
3476             d.closeEl.setDisplayed(opt.closable !== false);
3477             activeTextEl = textboxEl;
3478             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3479             if(opt.prompt){
3480                 if(opt.multiline){
3481                     textboxEl.hide();
3482                     textareaEl.show();
3483                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3484                         opt.multiline : this.defaultTextHeight);
3485                     activeTextEl = textareaEl;
3486                 }else{
3487                     textboxEl.show();
3488                     textareaEl.hide();
3489                 }
3490             }else{
3491                 textboxEl.hide();
3492                 textareaEl.hide();
3493             }
3494             progressEl.setDisplayed(opt.progress === true);
3495             this.updateProgress(0);
3496             activeTextEl.dom.value = opt.value || "";
3497             if(opt.prompt){
3498                 dlg.setDefaultButton(activeTextEl);
3499             }else{
3500                 var bs = opt.buttons;
3501                 var db = null;
3502                 if(bs && bs.ok){
3503                     db = buttons["ok"];
3504                 }else if(bs && bs.yes){
3505                     db = buttons["yes"];
3506                 }
3507                 dlg.setDefaultButton(db);
3508             }
3509             bwidth = updateButtons(opt.buttons);
3510             this.updateText(opt.msg);
3511             if(opt.cls){
3512                 d.el.addClass(opt.cls);
3513             }
3514             d.proxyDrag = opt.proxyDrag === true;
3515             d.modal = opt.modal !== false;
3516             d.mask = opt.modal !== false ? mask : false;
3517             if(!d.isVisible()){
3518                 // force it to the end of the z-index stack so it gets a cursor in FF
3519                 document.body.appendChild(dlg.el.dom);
3520                 d.animateTarget = null;
3521                 d.show(options.animEl);
3522             }
3523             return this;
3524         },
3525
3526         /**
3527          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3528          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529          * and closing the message box when the process is complete.
3530          * @param {String} title The title bar text
3531          * @param {String} msg The message box body text
3532          * @return {Roo.MessageBox} This message box
3533          */
3534         progress : function(title, msg){
3535             this.show({
3536                 title : title,
3537                 msg : msg,
3538                 buttons: false,
3539                 progress:true,
3540                 closable:false,
3541                 minWidth: this.minProgressWidth,
3542                 modal : true
3543             });
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549          * If a callback function is passed it will be called after the user clicks the button, and the
3550          * id of the button that was clicked will be passed as the only parameter to the callback
3551          * (could also be the top-right close button).
3552          * @param {String} title The title bar text
3553          * @param {String} msg The message box body text
3554          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555          * @param {Object} scope (optional) The scope of the callback function
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         alert : function(title, msg, fn, scope)
3559         {
3560             this.show({
3561                 title : title,
3562                 msg : msg,
3563                 buttons: this.OK,
3564                 fn: fn,
3565                 closable : false,
3566                 scope : scope,
3567                 modal : true
3568             });
3569             return this;
3570         },
3571
3572         /**
3573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575          * You are responsible for closing the message box when the process is complete.
3576          * @param {String} msg The message box body text
3577          * @param {String} title (optional) The title bar text
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         wait : function(msg, title){
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: false,
3585                 closable:false,
3586                 progress:true,
3587                 modal:true,
3588                 width:300,
3589                 wait:true
3590             });
3591             waitTimer = Roo.TaskMgr.start({
3592                 run: function(i){
3593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3594                 },
3595                 interval: 1000
3596             });
3597             return this;
3598         },
3599
3600         /**
3601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604          * @param {String} title The title bar text
3605          * @param {String} msg The message box body text
3606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607          * @param {Object} scope (optional) The scope of the callback function
3608          * @return {Roo.MessageBox} This message box
3609          */
3610         confirm : function(title, msg, fn, scope){
3611             this.show({
3612                 title : title,
3613                 msg : msg,
3614                 buttons: this.YESNO,
3615                 fn: fn,
3616                 scope : scope,
3617                 modal : true
3618             });
3619             return this;
3620         },
3621
3622         /**
3623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626          * (could also be the top-right close button) and the text that was entered will be passed as the two
3627          * parameters to the callback.
3628          * @param {String} title The title bar text
3629          * @param {String} msg The message box body text
3630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631          * @param {Object} scope (optional) The scope of the callback function
3632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634          * @return {Roo.MessageBox} This message box
3635          */
3636         prompt : function(title, msg, fn, scope, multiline){
3637             this.show({
3638                 title : title,
3639                 msg : msg,
3640                 buttons: this.OKCANCEL,
3641                 fn: fn,
3642                 minWidth:250,
3643                 scope : scope,
3644                 prompt:true,
3645                 multiline: multiline,
3646                 modal : true
3647             });
3648             return this;
3649         },
3650
3651         /**
3652          * Button config that displays a single OK button
3653          * @type Object
3654          */
3655         OK : {ok:true},
3656         /**
3657          * Button config that displays Yes and No buttons
3658          * @type Object
3659          */
3660         YESNO : {yes:true, no:true},
3661         /**
3662          * Button config that displays OK and Cancel buttons
3663          * @type Object
3664          */
3665         OKCANCEL : {ok:true, cancel:true},
3666         /**
3667          * Button config that displays Yes, No and Cancel buttons
3668          * @type Object
3669          */
3670         YESNOCANCEL : {yes:true, no:true, cancel:true},
3671
3672         /**
3673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3674          * @type Number
3675          */
3676         defaultTextHeight : 75,
3677         /**
3678          * The maximum width in pixels of the message box (defaults to 600)
3679          * @type Number
3680          */
3681         maxWidth : 600,
3682         /**
3683          * The minimum width in pixels of the message box (defaults to 100)
3684          * @type Number
3685          */
3686         minWidth : 100,
3687         /**
3688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3690          * @type Number
3691          */
3692         minProgressWidth : 250,
3693         /**
3694          * An object containing the default button text strings that can be overriden for localized language support.
3695          * Supported properties are: ok, cancel, yes and no.
3696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3697          * @type Object
3698          */
3699         buttonText : {
3700             ok : "OK",
3701             cancel : "Cancel",
3702             yes : "Yes",
3703             no : "No"
3704         }
3705     };
3706 }();
3707
3708 /**
3709  * Shorthand for {@link Roo.MessageBox}
3710  */
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3713 /*
3714  * - LGPL
3715  *
3716  * navbar
3717  * 
3718  */
3719
3720 /**
3721  * @class Roo.bootstrap.Navbar
3722  * @extends Roo.bootstrap.Component
3723  * Bootstrap Navbar class
3724
3725  * @constructor
3726  * Create a new Navbar
3727  * @param {Object} config The config object
3728  */
3729
3730
3731 Roo.bootstrap.Navbar = function(config){
3732     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3733     this.addEvents({
3734         // raw events
3735         /**
3736          * @event beforetoggle
3737          * Fire before toggle the menu
3738          * @param {Roo.EventObject} e
3739          */
3740         "beforetoggle" : true
3741     });
3742 };
3743
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3745     
3746     
3747    
3748     // private
3749     navItems : false,
3750     loadMask : false,
3751     
3752     
3753     getAutoCreate : function(){
3754         
3755         
3756         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3757         
3758     },
3759     
3760     initEvents :function ()
3761     {
3762         //Roo.log(this.el.select('.navbar-toggle',true));
3763         this.el.select('.navbar-toggle',true).on('click', function() {
3764             if(this.fireEvent('beforetoggle', this) !== false){
3765                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3766             }
3767             
3768         }, this);
3769         
3770         var mark = {
3771             tag: "div",
3772             cls:"x-dlg-mask"
3773         };
3774         
3775         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3776         
3777         var size = this.el.getSize();
3778         this.maskEl.setSize(size.width, size.height);
3779         this.maskEl.enableDisplayMode("block");
3780         this.maskEl.hide();
3781         
3782         if(this.loadMask){
3783             this.maskEl.show();
3784         }
3785     },
3786     
3787     
3788     getChildContainer : function()
3789     {
3790         if (this.el.select('.collapse').getCount()) {
3791             return this.el.select('.collapse',true).first();
3792         }
3793         
3794         return this.el;
3795     },
3796     
3797     mask : function()
3798     {
3799         this.maskEl.show();
3800     },
3801     
3802     unmask : function()
3803     {
3804         this.maskEl.hide();
3805     } 
3806     
3807     
3808     
3809     
3810 });
3811
3812
3813
3814  
3815
3816  /*
3817  * - LGPL
3818  *
3819  * navbar
3820  * 
3821  */
3822
3823 /**
3824  * @class Roo.bootstrap.NavSimplebar
3825  * @extends Roo.bootstrap.Navbar
3826  * Bootstrap Sidebar class
3827  *
3828  * @cfg {Boolean} inverse is inverted color
3829  * 
3830  * @cfg {String} type (nav | pills | tabs)
3831  * @cfg {Boolean} arrangement stacked | justified
3832  * @cfg {String} align (left | right) alignment
3833  * 
3834  * @cfg {Boolean} main (true|false) main nav bar? default false
3835  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3836  * 
3837  * @cfg {String} tag (header|footer|nav|div) default is nav 
3838
3839  * 
3840  * 
3841  * 
3842  * @constructor
3843  * Create a new Sidebar
3844  * @param {Object} config The config object
3845  */
3846
3847
3848 Roo.bootstrap.NavSimplebar = function(config){
3849     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3850 };
3851
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3853     
3854     inverse: false,
3855     
3856     type: false,
3857     arrangement: '',
3858     align : false,
3859     
3860     
3861     
3862     main : false,
3863     
3864     
3865     tag : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         var cfg = {
3872             tag : this.tag || 'div',
3873             cls : 'navbar'
3874         };
3875           
3876         
3877         cfg.cn = [
3878             {
3879                 cls: 'nav',
3880                 tag : 'ul'
3881             }
3882         ];
3883         
3884          
3885         this.type = this.type || 'nav';
3886         if (['tabs','pills'].indexOf(this.type)!==-1) {
3887             cfg.cn[0].cls += ' nav-' + this.type
3888         
3889         
3890         } else {
3891             if (this.type!=='nav') {
3892                 Roo.log('nav type must be nav/tabs/pills')
3893             }
3894             cfg.cn[0].cls += ' navbar-nav'
3895         }
3896         
3897         
3898         
3899         
3900         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901             cfg.cn[0].cls += ' nav-' + this.arrangement;
3902         }
3903         
3904         
3905         if (this.align === 'right') {
3906             cfg.cn[0].cls += ' navbar-right';
3907         }
3908         
3909         if (this.inverse) {
3910             cfg.cls += ' navbar-inverse';
3911             
3912         }
3913         
3914         
3915         return cfg;
3916     
3917         
3918     }
3919     
3920     
3921     
3922 });
3923
3924
3925
3926  
3927
3928  
3929        /*
3930  * - LGPL
3931  *
3932  * navbar
3933  * 
3934  */
3935
3936 /**
3937  * @class Roo.bootstrap.NavHeaderbar
3938  * @extends Roo.bootstrap.NavSimplebar
3939  * Bootstrap Sidebar class
3940  *
3941  * @cfg {String} brand what is brand
3942  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943  * @cfg {String} brand_href href of the brand
3944  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3945  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3948  * 
3949  * @constructor
3950  * Create a new Sidebar
3951  * @param {Object} config The config object
3952  */
3953
3954
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3957       
3958 };
3959
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3961     
3962     position: '',
3963     brand: '',
3964     brand_href: false,
3965     srButton : true,
3966     autohide : false,
3967     desktopCenter : false,
3968    
3969     
3970     getAutoCreate : function(){
3971         
3972         var   cfg = {
3973             tag: this.nav || 'nav',
3974             cls: 'navbar',
3975             role: 'navigation',
3976             cn: []
3977         };
3978         
3979         var cn = cfg.cn;
3980         if (this.desktopCenter) {
3981             cn.push({cls : 'container', cn : []});
3982             cn = cn[0].cn;
3983         }
3984         
3985         if(this.srButton){
3986             cn.push({
3987                 tag: 'div',
3988                 cls: 'navbar-header',
3989                 cn: [
3990                     {
3991                         tag: 'button',
3992                         type: 'button',
3993                         cls: 'navbar-toggle',
3994                         'data-toggle': 'collapse',
3995                         cn: [
3996                             {
3997                                 tag: 'span',
3998                                 cls: 'sr-only',
3999                                 html: 'Toggle navigation'
4000                             },
4001                             {
4002                                 tag: 'span',
4003                                 cls: 'icon-bar'
4004                             },
4005                             {
4006                                 tag: 'span',
4007                                 cls: 'icon-bar'
4008                             },
4009                             {
4010                                 tag: 'span',
4011                                 cls: 'icon-bar'
4012                             }
4013                         ]
4014                     }
4015                 ]
4016             });
4017         }
4018         
4019         cn.push({
4020             tag: 'div',
4021             cls: 'collapse navbar-collapse',
4022             cn : []
4023         });
4024         
4025         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4026         
4027         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028             cfg.cls += ' navbar-' + this.position;
4029             
4030             // tag can override this..
4031             
4032             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4033         }
4034         
4035         if (this.brand !== '') {
4036             cn[0].cn.push({
4037                 tag: 'a',
4038                 href: this.brand_href ? this.brand_href : '#',
4039                 cls: 'navbar-brand',
4040                 cn: [
4041                 this.brand
4042                 ]
4043             });
4044         }
4045         
4046         if(this.main){
4047             cfg.cls += ' main-nav';
4048         }
4049         
4050         
4051         return cfg;
4052
4053         
4054     },
4055     getHeaderChildContainer : function()
4056     {
4057         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058             return this.el.select('.navbar-header',true).first();
4059         }
4060         
4061         return this.getChildContainer();
4062     },
4063     
4064     
4065     initEvents : function()
4066     {
4067         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4068         
4069         if (this.autohide) {
4070             
4071             var prevScroll = 0;
4072             var ft = this.el;
4073             
4074             Roo.get(document).on('scroll',function(e) {
4075                 var ns = Roo.get(document).getScroll().top;
4076                 var os = prevScroll;
4077                 prevScroll = ns;
4078                 
4079                 if(ns > os){
4080                     ft.removeClass('slideDown');
4081                     ft.addClass('slideUp');
4082                     return;
4083                 }
4084                 ft.removeClass('slideUp');
4085                 ft.addClass('slideDown');
4086                  
4087               
4088           },this);
4089         }
4090     }    
4091     
4092 });
4093
4094
4095
4096  
4097
4098  /*
4099  * - LGPL
4100  *
4101  * navbar
4102  * 
4103  */
4104
4105 /**
4106  * @class Roo.bootstrap.NavSidebar
4107  * @extends Roo.bootstrap.Navbar
4108  * Bootstrap Sidebar class
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavSidebar = function(config){
4117     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4118 };
4119
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4121     
4122     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4123     
4124     getAutoCreate : function(){
4125         
4126         
4127         return  {
4128             tag: 'div',
4129             cls: 'sidebar sidebar-nav'
4130         };
4131     
4132         
4133     }
4134     
4135     
4136     
4137 });
4138
4139
4140
4141  
4142
4143  /*
4144  * - LGPL
4145  *
4146  * nav group
4147  * 
4148  */
4149
4150 /**
4151  * @class Roo.bootstrap.NavGroup
4152  * @extends Roo.bootstrap.Component
4153  * Bootstrap NavGroup class
4154  * @cfg {String} align (left|right)
4155  * @cfg {Boolean} inverse
4156  * @cfg {String} type (nav|pills|tab) default nav
4157  * @cfg {String} navId - reference Id for navbar.
4158
4159  * 
4160  * @constructor
4161  * Create a new nav group
4162  * @param {Object} config The config object
4163  */
4164
4165 Roo.bootstrap.NavGroup = function(config){
4166     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4167     this.navItems = [];
4168    
4169     Roo.bootstrap.NavGroup.register(this);
4170      this.addEvents({
4171         /**
4172              * @event changed
4173              * Fires when the active item changes
4174              * @param {Roo.bootstrap.NavGroup} this
4175              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4177          */
4178         'changed': true
4179      });
4180     
4181 };
4182
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4184     
4185     align: '',
4186     inverse: false,
4187     form: false,
4188     type: 'nav',
4189     navId : '',
4190     // private
4191     
4192     navItems : false, 
4193     
4194     getAutoCreate : function()
4195     {
4196         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4197         
4198         cfg = {
4199             tag : 'ul',
4200             cls: 'nav' 
4201         };
4202         
4203         if (['tabs','pills'].indexOf(this.type)!==-1) {
4204             cfg.cls += ' nav-' + this.type
4205         } else {
4206             if (this.type!=='nav') {
4207                 Roo.log('nav type must be nav/tabs/pills')
4208             }
4209             cfg.cls += ' navbar-nav'
4210         }
4211         
4212         if (this.parent() && this.parent().sidebar) {
4213             cfg = {
4214                 tag: 'ul',
4215                 cls: 'dashboard-menu sidebar-menu'
4216             };
4217             
4218             return cfg;
4219         }
4220         
4221         if (this.form === true) {
4222             cfg = {
4223                 tag: 'form',
4224                 cls: 'navbar-form'
4225             };
4226             
4227             if (this.align === 'right') {
4228                 cfg.cls += ' navbar-right';
4229             } else {
4230                 cfg.cls += ' navbar-left';
4231             }
4232         }
4233         
4234         if (this.align === 'right') {
4235             cfg.cls += ' navbar-right';
4236         }
4237         
4238         if (this.inverse) {
4239             cfg.cls += ' navbar-inverse';
4240             
4241         }
4242         
4243         
4244         return cfg;
4245     },
4246     /**
4247     * sets the active Navigation item
4248     * @param {Roo.bootstrap.NavItem} the new current navitem
4249     */
4250     setActiveItem : function(item)
4251     {
4252         var prev = false;
4253         Roo.each(this.navItems, function(v){
4254             if (v == item) {
4255                 return ;
4256             }
4257             if (v.isActive()) {
4258                 v.setActive(false, true);
4259                 prev = v;
4260                 
4261             }
4262             
4263         });
4264
4265         item.setActive(true, true);
4266         this.fireEvent('changed', this, item, prev);
4267         
4268         
4269     },
4270     /**
4271     * gets the active Navigation item
4272     * @return {Roo.bootstrap.NavItem} the current navitem
4273     */
4274     getActive : function()
4275     {
4276         
4277         var prev = false;
4278         Roo.each(this.navItems, function(v){
4279             
4280             if (v.isActive()) {
4281                 prev = v;
4282                 
4283             }
4284             
4285         });
4286         return prev;
4287     },
4288     
4289     indexOfNav : function()
4290     {
4291         
4292         var prev = false;
4293         Roo.each(this.navItems, function(v,i){
4294             
4295             if (v.isActive()) {
4296                 prev = i;
4297                 
4298             }
4299             
4300         });
4301         return prev;
4302     },
4303     /**
4304     * adds a Navigation item
4305     * @param {Roo.bootstrap.NavItem} the navitem to add
4306     */
4307     addItem : function(cfg)
4308     {
4309         var cn = new Roo.bootstrap.NavItem(cfg);
4310         this.register(cn);
4311         cn.parentId = this.id;
4312         cn.onRender(this.el, null);
4313         return cn;
4314     },
4315     /**
4316     * register a Navigation item
4317     * @param {Roo.bootstrap.NavItem} the navitem to add
4318     */
4319     register : function(item)
4320     {
4321         this.navItems.push( item);
4322         item.navId = this.navId;
4323     
4324     },
4325     
4326     /**
4327     * clear all the Navigation item
4328     */
4329    
4330     clearAll : function()
4331     {
4332         this.navItems = [];
4333         this.el.dom.innerHTML = '';
4334     },
4335     
4336     getNavItem: function(tabId)
4337     {
4338         var ret = false;
4339         Roo.each(this.navItems, function(e) {
4340             if (e.tabId == tabId) {
4341                ret =  e;
4342                return false;
4343             }
4344             return true;
4345             
4346         });
4347         return ret;
4348     },
4349     
4350     setActiveNext : function()
4351     {
4352         var i = this.indexOfNav(this.getActive());
4353         if (i > this.navItems.length) {
4354             return;
4355         }
4356         this.setActiveItem(this.navItems[i+1]);
4357     },
4358     setActivePrev : function()
4359     {
4360         var i = this.indexOfNav(this.getActive());
4361         if (i  < 1) {
4362             return;
4363         }
4364         this.setActiveItem(this.navItems[i-1]);
4365     },
4366     clearWasActive : function(except) {
4367         Roo.each(this.navItems, function(e) {
4368             if (e.tabId != except.tabId && e.was_active) {
4369                e.was_active = false;
4370                return false;
4371             }
4372             return true;
4373             
4374         });
4375     },
4376     getWasActive : function ()
4377     {
4378         var r = false;
4379         Roo.each(this.navItems, function(e) {
4380             if (e.was_active) {
4381                r = e;
4382                return false;
4383             }
4384             return true;
4385             
4386         });
4387         return r;
4388     }
4389     
4390     
4391 });
4392
4393  
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4395     
4396     groups: {},
4397      /**
4398     * register a Navigation Group
4399     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4400     */
4401     register : function(navgrp)
4402     {
4403         this.groups[navgrp.navId] = navgrp;
4404         
4405     },
4406     /**
4407     * fetch a Navigation Group based on the navigation ID
4408     * @param {string} the navgroup to add
4409     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4410     */
4411     get: function(navId) {
4412         if (typeof(this.groups[navId]) == 'undefined') {
4413             return false;
4414             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4415         }
4416         return this.groups[navId] ;
4417     }
4418     
4419     
4420     
4421 });
4422
4423  /*
4424  * - LGPL
4425  *
4426  * row
4427  * 
4428  */
4429
4430 /**
4431  * @class Roo.bootstrap.NavItem
4432  * @extends Roo.bootstrap.Component
4433  * Bootstrap Navbar.NavItem class
4434  * @cfg {String} href  link to
4435  * @cfg {String} html content of button
4436  * @cfg {String} badge text inside badge
4437  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438  * @cfg {String} glyphicon name of glyphicon
4439  * @cfg {String} icon name of font awesome icon
4440  * @cfg {Boolean} active Is item active
4441  * @cfg {Boolean} disabled Is item disabled
4442  
4443  * @cfg {Boolean} preventDefault (true | false) default false
4444  * @cfg {String} tabId the tab that this item activates.
4445  * @cfg {String} tagtype (a|span) render as a href or span?
4446  * @cfg {Boolean} animateRef (true|false) link to element default false  
4447   
4448  * @constructor
4449  * Create a new Navbar Item
4450  * @param {Object} config The config object
4451  */
4452 Roo.bootstrap.NavItem = function(config){
4453     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4454     this.addEvents({
4455         // raw events
4456         /**
4457          * @event click
4458          * The raw click event for the entire grid.
4459          * @param {Roo.EventObject} e
4460          */
4461         "click" : true,
4462          /**
4463             * @event changed
4464             * Fires when the active item active state changes
4465             * @param {Roo.bootstrap.NavItem} this
4466             * @param {boolean} state the new state
4467              
4468          */
4469         'changed': true,
4470         /**
4471             * @event scrollto
4472             * Fires when scroll to element
4473             * @param {Roo.bootstrap.NavItem} this
4474             * @param {Object} options
4475             * @param {Roo.EventObject} e
4476              
4477          */
4478         'scrollto': true
4479     });
4480    
4481 };
4482
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4484     
4485     href: false,
4486     html: '',
4487     badge: '',
4488     icon: false,
4489     glyphicon: false,
4490     active: false,
4491     preventDefault : false,
4492     tabId : false,
4493     tagtype : 'a',
4494     disabled : false,
4495     animateRef : false,
4496     was_active : false,
4497     
4498     getAutoCreate : function(){
4499          
4500         var cfg = {
4501             tag: 'li',
4502             cls: 'nav-item'
4503             
4504         };
4505         
4506         if (this.active) {
4507             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4508         }
4509         if (this.disabled) {
4510             cfg.cls += ' disabled';
4511         }
4512         
4513         if (this.href || this.html || this.glyphicon || this.icon) {
4514             cfg.cn = [
4515                 {
4516                     tag: this.tagtype,
4517                     href : this.href || "#",
4518                     html: this.html || ''
4519                 }
4520             ];
4521             
4522             if (this.icon) {
4523                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4524             }
4525
4526             if(this.glyphicon) {
4527                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4528             }
4529             
4530             if (this.menu) {
4531                 
4532                 cfg.cn[0].html += " <span class='caret'></span>";
4533              
4534             }
4535             
4536             if (this.badge !== '') {
4537                  
4538                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4539             }
4540         }
4541         
4542         
4543         
4544         return cfg;
4545     },
4546     initEvents: function() 
4547     {
4548         if (typeof (this.menu) != 'undefined') {
4549             this.menu.parentType = this.xtype;
4550             this.menu.triggerEl = this.el;
4551             this.menu = this.addxtype(Roo.apply({}, this.menu));
4552         }
4553         
4554         this.el.select('a',true).on('click', this.onClick, this);
4555         
4556         if(this.tagtype == 'span'){
4557             this.el.select('span',true).on('click', this.onClick, this);
4558         }
4559        
4560         // at this point parent should be available..
4561         this.parent().register(this);
4562     },
4563     
4564     onClick : function(e)
4565     {
4566         if (e.getTarget('.dropdown-menu-item')) {
4567             // did you click on a menu itemm.... - then don't trigger onclick..
4568             return;
4569         }
4570         
4571         if(
4572                 this.preventDefault || 
4573                 this.href == '#' 
4574         ){
4575             Roo.log("NavItem - prevent Default?");
4576             e.preventDefault();
4577         }
4578         
4579         if (this.disabled) {
4580             return;
4581         }
4582         
4583         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584         if (tg && tg.transition) {
4585             Roo.log("waiting for the transitionend");
4586             return;
4587         }
4588         
4589         
4590         
4591         //Roo.log("fire event clicked");
4592         if(this.fireEvent('click', this, e) === false){
4593             return;
4594         };
4595         
4596         if(this.tagtype == 'span'){
4597             return;
4598         }
4599         
4600         //Roo.log(this.href);
4601         var ael = this.el.select('a',true).first();
4602         //Roo.log(ael);
4603         
4604         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607                 return; // ignore... - it's a 'hash' to another page.
4608             }
4609             Roo.log("NavItem - prevent Default?");
4610             e.preventDefault();
4611             this.scrollToElement(e);
4612         }
4613         
4614         
4615         var p =  this.parent();
4616    
4617         if (['tabs','pills'].indexOf(p.type)!==-1) {
4618             if (typeof(p.setActiveItem) !== 'undefined') {
4619                 p.setActiveItem(this);
4620             }
4621         }
4622         
4623         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625             // remove the collapsed menu expand...
4626             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4627         }
4628     },
4629     
4630     isActive: function () {
4631         return this.active
4632     },
4633     setActive : function(state, fire, is_was_active)
4634     {
4635         if (this.active && !state && this.navId) {
4636             this.was_active = true;
4637             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4638             if (nv) {
4639                 nv.clearWasActive(this);
4640             }
4641             
4642         }
4643         this.active = state;
4644         
4645         if (!state ) {
4646             this.el.removeClass('active');
4647         } else if (!this.el.hasClass('active')) {
4648             this.el.addClass('active');
4649         }
4650         if (fire) {
4651             this.fireEvent('changed', this, state);
4652         }
4653         
4654         // show a panel if it's registered and related..
4655         
4656         if (!this.navId || !this.tabId || !state || is_was_active) {
4657             return;
4658         }
4659         
4660         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4661         if (!tg) {
4662             return;
4663         }
4664         var pan = tg.getPanelByName(this.tabId);
4665         if (!pan) {
4666             return;
4667         }
4668         // if we can not flip to new panel - go back to old nav highlight..
4669         if (false == tg.showPanel(pan)) {
4670             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4671             if (nv) {
4672                 var onav = nv.getWasActive();
4673                 if (onav) {
4674                     onav.setActive(true, false, true);
4675                 }
4676             }
4677             
4678         }
4679         
4680         
4681         
4682     },
4683      // this should not be here...
4684     setDisabled : function(state)
4685     {
4686         this.disabled = state;
4687         if (!state ) {
4688             this.el.removeClass('disabled');
4689         } else if (!this.el.hasClass('disabled')) {
4690             this.el.addClass('disabled');
4691         }
4692         
4693     },
4694     
4695     /**
4696      * Fetch the element to display the tooltip on.
4697      * @return {Roo.Element} defaults to this.el
4698      */
4699     tooltipEl : function()
4700     {
4701         return this.el.select('' + this.tagtype + '', true).first();
4702     },
4703     
4704     scrollToElement : function(e)
4705     {
4706         var c = document.body;
4707         
4708         /*
4709          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4710          */
4711         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712             c = document.documentElement;
4713         }
4714         
4715         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4716         
4717         if(!target){
4718             return;
4719         }
4720
4721         var o = target.calcOffsetsTo(c);
4722         
4723         var options = {
4724             target : target,
4725             value : o[1]
4726         };
4727         
4728         this.fireEvent('scrollto', this, options, e);
4729         
4730         Roo.get(c).scrollTo('top', options.value, true);
4731         
4732         return;
4733     }
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * sidebar item
4741  *
4742  *  li
4743  *    <span> icon </span>
4744  *    <span> text </span>
4745  *    <span>badge </span>
4746  */
4747
4748 /**
4749  * @class Roo.bootstrap.NavSidebarItem
4750  * @extends Roo.bootstrap.NavItem
4751  * Bootstrap Navbar.NavSidebarItem class
4752  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753  * {Boolean} open is the menu open
4754  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756  * {String} buttonSize (sm|md|lg)the extra classes for the button
4757  * {Boolean} showArrow show arrow next to the text (default true)
4758  * @constructor
4759  * Create a new Navbar Button
4760  * @param {Object} config The config object
4761  */
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4764     this.addEvents({
4765         // raw events
4766         /**
4767          * @event click
4768          * The raw click event for the entire grid.
4769          * @param {Roo.EventObject} e
4770          */
4771         "click" : true,
4772          /**
4773             * @event changed
4774             * Fires when the active item active state changes
4775             * @param {Roo.bootstrap.NavSidebarItem} this
4776             * @param {boolean} state the new state
4777              
4778          */
4779         'changed': true
4780     });
4781    
4782 };
4783
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4785     
4786     badgeWeight : 'default',
4787     
4788     open: false,
4789     
4790     buttonView : false,
4791     
4792     buttonWeight : 'default',
4793     
4794     buttonSize : 'md',
4795     
4796     showArrow : true,
4797     
4798     getAutoCreate : function(){
4799         
4800         
4801         var a = {
4802                 tag: 'a',
4803                 href : this.href || '#',
4804                 cls: '',
4805                 html : '',
4806                 cn : []
4807         };
4808         
4809         if(this.buttonView){
4810             a = {
4811                 tag: 'button',
4812                 href : this.href || '#',
4813                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4814                 html : this.html,
4815                 cn : []
4816             };
4817         }
4818         
4819         var cfg = {
4820             tag: 'li',
4821             cls: '',
4822             cn: [ a ]
4823         };
4824         
4825         if (this.active) {
4826             cfg.cls += ' active';
4827         }
4828         
4829         if (this.disabled) {
4830             cfg.cls += ' disabled';
4831         }
4832         if (this.open) {
4833             cfg.cls += ' open x-open';
4834         }
4835         // left icon..
4836         if (this.glyphicon || this.icon) {
4837             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4838             a.cn.push({ tag : 'i', cls : c }) ;
4839         }
4840         
4841         if(!this.buttonView){
4842             var span = {
4843                 tag: 'span',
4844                 html : this.html || ''
4845             };
4846
4847             a.cn.push(span);
4848             
4849         }
4850         
4851         if (this.badge !== '') {
4852             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4853         }
4854         
4855         if (this.menu) {
4856             
4857             if(this.showArrow){
4858                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4859             }
4860             
4861             a.cls += ' dropdown-toggle treeview' ;
4862         }
4863         
4864         return cfg;
4865     },
4866     
4867     initEvents : function()
4868     { 
4869         if (typeof (this.menu) != 'undefined') {
4870             this.menu.parentType = this.xtype;
4871             this.menu.triggerEl = this.el;
4872             this.menu = this.addxtype(Roo.apply({}, this.menu));
4873         }
4874         
4875         this.el.on('click', this.onClick, this);
4876         
4877         if(this.badge !== ''){
4878             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4879         }
4880         
4881     },
4882     
4883     onClick : function(e)
4884     {
4885         if(this.disabled){
4886             e.preventDefault();
4887             return;
4888         }
4889         
4890         if(this.preventDefault){
4891             e.preventDefault();
4892         }
4893         
4894         this.fireEvent('click', this);
4895     },
4896     
4897     disable : function()
4898     {
4899         this.setDisabled(true);
4900     },
4901     
4902     enable : function()
4903     {
4904         this.setDisabled(false);
4905     },
4906     
4907     setDisabled : function(state)
4908     {
4909         if(this.disabled == state){
4910             return;
4911         }
4912         
4913         this.disabled = state;
4914         
4915         if (state) {
4916             this.el.addClass('disabled');
4917             return;
4918         }
4919         
4920         this.el.removeClass('disabled');
4921         
4922         return;
4923     },
4924     
4925     setActive : function(state)
4926     {
4927         if(this.active == state){
4928             return;
4929         }
4930         
4931         this.active = state;
4932         
4933         if (state) {
4934             this.el.addClass('active');
4935             return;
4936         }
4937         
4938         this.el.removeClass('active');
4939         
4940         return;
4941     },
4942     
4943     isActive: function () 
4944     {
4945         return this.active;
4946     },
4947     
4948     setBadge : function(str)
4949     {
4950         if(!this.badgeEl){
4951             return;
4952         }
4953         
4954         this.badgeEl.dom.innerHTML = str;
4955     }
4956     
4957    
4958      
4959  
4960 });
4961  
4962
4963  /*
4964  * - LGPL
4965  *
4966  * row
4967  * 
4968  */
4969
4970 /**
4971  * @class Roo.bootstrap.Row
4972  * @extends Roo.bootstrap.Component
4973  * Bootstrap Row class (contains columns...)
4974  * 
4975  * @constructor
4976  * Create a new Row
4977  * @param {Object} config The config object
4978  */
4979
4980 Roo.bootstrap.Row = function(config){
4981     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4982 };
4983
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4985     
4986     getAutoCreate : function(){
4987        return {
4988             cls: 'row clearfix'
4989        };
4990     }
4991     
4992     
4993 });
4994
4995  
4996
4997  /*
4998  * - LGPL
4999  *
5000  * element
5001  * 
5002  */
5003
5004 /**
5005  * @class Roo.bootstrap.Element
5006  * @extends Roo.bootstrap.Component
5007  * Bootstrap Element class
5008  * @cfg {String} html contents of the element
5009  * @cfg {String} tag tag of the element
5010  * @cfg {String} cls class of the element
5011  * @cfg {Boolean} preventDefault (true|false) default false
5012  * @cfg {Boolean} clickable (true|false) default false
5013  * 
5014  * @constructor
5015  * Create a new Element
5016  * @param {Object} config The config object
5017  */
5018
5019 Roo.bootstrap.Element = function(config){
5020     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5021     
5022     this.addEvents({
5023         // raw events
5024         /**
5025          * @event click
5026          * When a element is chick
5027          * @param {Roo.bootstrap.Element} this
5028          * @param {Roo.EventObject} e
5029          */
5030         "click" : true
5031     });
5032 };
5033
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5035     
5036     tag: 'div',
5037     cls: '',
5038     html: '',
5039     preventDefault: false, 
5040     clickable: false,
5041     
5042     getAutoCreate : function(){
5043         
5044         var cfg = {
5045             tag: this.tag,
5046             // cls: this.cls, double assign in parent class Component.js :: onRender
5047             html: this.html
5048         };
5049         
5050         return cfg;
5051     },
5052     
5053     initEvents: function() 
5054     {
5055         Roo.bootstrap.Element.superclass.initEvents.call(this);
5056         
5057         if(this.clickable){
5058             this.el.on('click', this.onClick, this);
5059         }
5060         
5061     },
5062     
5063     onClick : function(e)
5064     {
5065         if(this.preventDefault){
5066             e.preventDefault();
5067         }
5068         
5069         this.fireEvent('click', this, e);
5070     },
5071     
5072     getValue : function()
5073     {
5074         return this.el.dom.innerHTML;
5075     },
5076     
5077     setValue : function(value)
5078     {
5079         this.el.dom.innerHTML = value;
5080     }
5081    
5082 });
5083
5084  
5085
5086  /*
5087  * - LGPL
5088  *
5089  * pagination
5090  * 
5091  */
5092
5093 /**
5094  * @class Roo.bootstrap.Pagination
5095  * @extends Roo.bootstrap.Component
5096  * Bootstrap Pagination class
5097  * @cfg {String} size xs | sm | md | lg
5098  * @cfg {Boolean} inverse false | true
5099  * 
5100  * @constructor
5101  * Create a new Pagination
5102  * @param {Object} config The config object
5103  */
5104
5105 Roo.bootstrap.Pagination = function(config){
5106     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5107 };
5108
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5110     
5111     cls: false,
5112     size: false,
5113     inverse: false,
5114     
5115     getAutoCreate : function(){
5116         var cfg = {
5117             tag: 'ul',
5118                 cls: 'pagination'
5119         };
5120         if (this.inverse) {
5121             cfg.cls += ' inverse';
5122         }
5123         if (this.html) {
5124             cfg.html=this.html;
5125         }
5126         if (this.cls) {
5127             cfg.cls += " " + this.cls;
5128         }
5129         return cfg;
5130     }
5131    
5132 });
5133
5134  
5135
5136  /*
5137  * - LGPL
5138  *
5139  * Pagination item
5140  * 
5141  */
5142
5143
5144 /**
5145  * @class Roo.bootstrap.PaginationItem
5146  * @extends Roo.bootstrap.Component
5147  * Bootstrap PaginationItem class
5148  * @cfg {String} html text
5149  * @cfg {String} href the link
5150  * @cfg {Boolean} preventDefault (true | false) default true
5151  * @cfg {Boolean} active (true | false) default false
5152  * @cfg {Boolean} disabled default false
5153  * 
5154  * 
5155  * @constructor
5156  * Create a new PaginationItem
5157  * @param {Object} config The config object
5158  */
5159
5160
5161 Roo.bootstrap.PaginationItem = function(config){
5162     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5163     this.addEvents({
5164         // raw events
5165         /**
5166          * @event click
5167          * The raw click event for the entire grid.
5168          * @param {Roo.EventObject} e
5169          */
5170         "click" : true
5171     });
5172 };
5173
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5175     
5176     href : false,
5177     html : false,
5178     preventDefault: true,
5179     active : false,
5180     cls : false,
5181     disabled: false,
5182     
5183     getAutoCreate : function(){
5184         var cfg= {
5185             tag: 'li',
5186             cn: [
5187                 {
5188                     tag : 'a',
5189                     href : this.href ? this.href : '#',
5190                     html : this.html ? this.html : ''
5191                 }
5192             ]
5193         };
5194         
5195         if(this.cls){
5196             cfg.cls = this.cls;
5197         }
5198         
5199         if(this.disabled){
5200             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5201         }
5202         
5203         if(this.active){
5204             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5205         }
5206         
5207         return cfg;
5208     },
5209     
5210     initEvents: function() {
5211         
5212         this.el.on('click', this.onClick, this);
5213         
5214     },
5215     onClick : function(e)
5216     {
5217         Roo.log('PaginationItem on click ');
5218         if(this.preventDefault){
5219             e.preventDefault();
5220         }
5221         
5222         if(this.disabled){
5223             return;
5224         }
5225         
5226         this.fireEvent('click', this, e);
5227     }
5228    
5229 });
5230
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * slider
5237  * 
5238  */
5239
5240
5241 /**
5242  * @class Roo.bootstrap.Slider
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Slider class
5245  *    
5246  * @constructor
5247  * Create a new Slider
5248  * @param {Object} config The config object
5249  */
5250
5251 Roo.bootstrap.Slider = function(config){
5252     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5253 };
5254
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5256     
5257     getAutoCreate : function(){
5258         
5259         var cfg = {
5260             tag: 'div',
5261             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5262             cn: [
5263                 {
5264                     tag: 'a',
5265                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5266                 }
5267             ]
5268         };
5269         
5270         return cfg;
5271     }
5272    
5273 });
5274
5275  /*
5276  * Based on:
5277  * Ext JS Library 1.1.1
5278  * Copyright(c) 2006-2007, Ext JS, LLC.
5279  *
5280  * Originally Released Under LGPL - original licence link has changed is not relivant.
5281  *
5282  * Fork - LGPL
5283  * <script type="text/javascript">
5284  */
5285  
5286
5287 /**
5288  * @class Roo.grid.ColumnModel
5289  * @extends Roo.util.Observable
5290  * This is the default implementation of a ColumnModel used by the Grid. It defines
5291  * the columns in the grid.
5292  * <br>Usage:<br>
5293  <pre><code>
5294  var colModel = new Roo.grid.ColumnModel([
5295         {header: "Ticker", width: 60, sortable: true, locked: true},
5296         {header: "Company Name", width: 150, sortable: true},
5297         {header: "Market Cap.", width: 100, sortable: true},
5298         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299         {header: "Employees", width: 100, sortable: true, resizable: false}
5300  ]);
5301  </code></pre>
5302  * <p>
5303  
5304  * The config options listed for this class are options which may appear in each
5305  * individual column definition.
5306  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5307  * @constructor
5308  * @param {Object} config An Array of column config objects. See this class's
5309  * config objects for details.
5310 */
5311 Roo.grid.ColumnModel = function(config){
5312         /**
5313      * The config passed into the constructor
5314      */
5315     this.config = config;
5316     this.lookup = {};
5317
5318     // if no id, create one
5319     // if the column does not have a dataIndex mapping,
5320     // map it to the order it is in the config
5321     for(var i = 0, len = config.length; i < len; i++){
5322         var c = config[i];
5323         if(typeof c.dataIndex == "undefined"){
5324             c.dataIndex = i;
5325         }
5326         if(typeof c.renderer == "string"){
5327             c.renderer = Roo.util.Format[c.renderer];
5328         }
5329         if(typeof c.id == "undefined"){
5330             c.id = Roo.id();
5331         }
5332         if(c.editor && c.editor.xtype){
5333             c.editor  = Roo.factory(c.editor, Roo.grid);
5334         }
5335         if(c.editor && c.editor.isFormField){
5336             c.editor = new Roo.grid.GridEditor(c.editor);
5337         }
5338         this.lookup[c.id] = c;
5339     }
5340
5341     /**
5342      * The width of columns which have no width specified (defaults to 100)
5343      * @type Number
5344      */
5345     this.defaultWidth = 100;
5346
5347     /**
5348      * Default sortable of columns which have no sortable specified (defaults to false)
5349      * @type Boolean
5350      */
5351     this.defaultSortable = false;
5352
5353     this.addEvents({
5354         /**
5355              * @event widthchange
5356              * Fires when the width of a column changes.
5357              * @param {ColumnModel} this
5358              * @param {Number} columnIndex The column index
5359              * @param {Number} newWidth The new width
5360              */
5361             "widthchange": true,
5362         /**
5363              * @event headerchange
5364              * Fires when the text of a header changes.
5365              * @param {ColumnModel} this
5366              * @param {Number} columnIndex The column index
5367              * @param {Number} newText The new header text
5368              */
5369             "headerchange": true,
5370         /**
5371              * @event hiddenchange
5372              * Fires when a column is hidden or "unhidden".
5373              * @param {ColumnModel} this
5374              * @param {Number} columnIndex The column index
5375              * @param {Boolean} hidden true if hidden, false otherwise
5376              */
5377             "hiddenchange": true,
5378             /**
5379          * @event columnmoved
5380          * Fires when a column is moved.
5381          * @param {ColumnModel} this
5382          * @param {Number} oldIndex
5383          * @param {Number} newIndex
5384          */
5385         "columnmoved" : true,
5386         /**
5387          * @event columlockchange
5388          * Fires when a column's locked state is changed
5389          * @param {ColumnModel} this
5390          * @param {Number} colIndex
5391          * @param {Boolean} locked true if locked
5392          */
5393         "columnlockchange" : true
5394     });
5395     Roo.grid.ColumnModel.superclass.constructor.call(this);
5396 };
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5398     /**
5399      * @cfg {String} header The header text to display in the Grid view.
5400      */
5401     /**
5402      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404      * specified, the column's index is used as an index into the Record's data Array.
5405      */
5406     /**
5407      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5409      */
5410     /**
5411      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412      * Defaults to the value of the {@link #defaultSortable} property.
5413      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5414      */
5415     /**
5416      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5417      */
5418     /**
5419      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5420      */
5421     /**
5422      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5423      */
5424     /**
5425      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5426      */
5427     /**
5428      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5432      */
5433        /**
5434      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5435      */
5436     /**
5437      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5438      */
5439     /**
5440      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5441      */
5442     /**
5443      * @cfg {String} cursor (Optional)
5444      */
5445     /**
5446      * @cfg {String} tooltip (Optional)
5447      */
5448     /**
5449      * @cfg {Number} xs (Optional)
5450      */
5451     /**
5452      * @cfg {Number} sm (Optional)
5453      */
5454     /**
5455      * @cfg {Number} md (Optional)
5456      */
5457     /**
5458      * @cfg {Number} lg (Optional)
5459      */
5460     /**
5461      * Returns the id of the column at the specified index.
5462      * @param {Number} index The column index
5463      * @return {String} the id
5464      */
5465     getColumnId : function(index){
5466         return this.config[index].id;
5467     },
5468
5469     /**
5470      * Returns the column for a specified id.
5471      * @param {String} id The column id
5472      * @return {Object} the column
5473      */
5474     getColumnById : function(id){
5475         return this.lookup[id];
5476     },
5477
5478     
5479     /**
5480      * Returns the column for a specified dataIndex.
5481      * @param {String} dataIndex The column dataIndex
5482      * @return {Object|Boolean} the column or false if not found
5483      */
5484     getColumnByDataIndex: function(dataIndex){
5485         var index = this.findColumnIndex(dataIndex);
5486         return index > -1 ? this.config[index] : false;
5487     },
5488     
5489     /**
5490      * Returns the index for a specified column id.
5491      * @param {String} id The column id
5492      * @return {Number} the index, or -1 if not found
5493      */
5494     getIndexById : function(id){
5495         for(var i = 0, len = this.config.length; i < len; i++){
5496             if(this.config[i].id == id){
5497                 return i;
5498             }
5499         }
5500         return -1;
5501     },
5502     
5503     /**
5504      * Returns the index for a specified column dataIndex.
5505      * @param {String} dataIndex The column dataIndex
5506      * @return {Number} the index, or -1 if not found
5507      */
5508     
5509     findColumnIndex : function(dataIndex){
5510         for(var i = 0, len = this.config.length; i < len; i++){
5511             if(this.config[i].dataIndex == dataIndex){
5512                 return i;
5513             }
5514         }
5515         return -1;
5516     },
5517     
5518     
5519     moveColumn : function(oldIndex, newIndex){
5520         var c = this.config[oldIndex];
5521         this.config.splice(oldIndex, 1);
5522         this.config.splice(newIndex, 0, c);
5523         this.dataMap = null;
5524         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5525     },
5526
5527     isLocked : function(colIndex){
5528         return this.config[colIndex].locked === true;
5529     },
5530
5531     setLocked : function(colIndex, value, suppressEvent){
5532         if(this.isLocked(colIndex) == value){
5533             return;
5534         }
5535         this.config[colIndex].locked = value;
5536         if(!suppressEvent){
5537             this.fireEvent("columnlockchange", this, colIndex, value);
5538         }
5539     },
5540
5541     getTotalLockedWidth : function(){
5542         var totalWidth = 0;
5543         for(var i = 0; i < this.config.length; i++){
5544             if(this.isLocked(i) && !this.isHidden(i)){
5545                 this.totalWidth += this.getColumnWidth(i);
5546             }
5547         }
5548         return totalWidth;
5549     },
5550
5551     getLockedCount : function(){
5552         for(var i = 0, len = this.config.length; i < len; i++){
5553             if(!this.isLocked(i)){
5554                 return i;
5555             }
5556         }
5557         
5558         return this.config.length;
5559     },
5560
5561     /**
5562      * Returns the number of columns.
5563      * @return {Number}
5564      */
5565     getColumnCount : function(visibleOnly){
5566         if(visibleOnly === true){
5567             var c = 0;
5568             for(var i = 0, len = this.config.length; i < len; i++){
5569                 if(!this.isHidden(i)){
5570                     c++;
5571                 }
5572             }
5573             return c;
5574         }
5575         return this.config.length;
5576     },
5577
5578     /**
5579      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580      * @param {Function} fn
5581      * @param {Object} scope (optional)
5582      * @return {Array} result
5583      */
5584     getColumnsBy : function(fn, scope){
5585         var r = [];
5586         for(var i = 0, len = this.config.length; i < len; i++){
5587             var c = this.config[i];
5588             if(fn.call(scope||this, c, i) === true){
5589                 r[r.length] = c;
5590             }
5591         }
5592         return r;
5593     },
5594
5595     /**
5596      * Returns true if the specified column is sortable.
5597      * @param {Number} col The column index
5598      * @return {Boolean}
5599      */
5600     isSortable : function(col){
5601         if(typeof this.config[col].sortable == "undefined"){
5602             return this.defaultSortable;
5603         }
5604         return this.config[col].sortable;
5605     },
5606
5607     /**
5608      * Returns the rendering (formatting) function defined for the column.
5609      * @param {Number} col The column index.
5610      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5611      */
5612     getRenderer : function(col){
5613         if(!this.config[col].renderer){
5614             return Roo.grid.ColumnModel.defaultRenderer;
5615         }
5616         return this.config[col].renderer;
5617     },
5618
5619     /**
5620      * Sets the rendering (formatting) function for a column.
5621      * @param {Number} col The column index
5622      * @param {Function} fn The function to use to process the cell's raw data
5623      * to return HTML markup for the grid view. The render function is called with
5624      * the following parameters:<ul>
5625      * <li>Data value.</li>
5626      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627      * <li>css A CSS style string to apply to the table cell.</li>
5628      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630      * <li>Row index</li>
5631      * <li>Column index</li>
5632      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5633      */
5634     setRenderer : function(col, fn){
5635         this.config[col].renderer = fn;
5636     },
5637
5638     /**
5639      * Returns the width for the specified column.
5640      * @param {Number} col The column index
5641      * @return {Number}
5642      */
5643     getColumnWidth : function(col){
5644         return this.config[col].width * 1 || this.defaultWidth;
5645     },
5646
5647     /**
5648      * Sets the width for a column.
5649      * @param {Number} col The column index
5650      * @param {Number} width The new width
5651      */
5652     setColumnWidth : function(col, width, suppressEvent){
5653         this.config[col].width = width;
5654         this.totalWidth = null;
5655         if(!suppressEvent){
5656              this.fireEvent("widthchange", this, col, width);
5657         }
5658     },
5659
5660     /**
5661      * Returns the total width of all columns.
5662      * @param {Boolean} includeHidden True to include hidden column widths
5663      * @return {Number}
5664      */
5665     getTotalWidth : function(includeHidden){
5666         if(!this.totalWidth){
5667             this.totalWidth = 0;
5668             for(var i = 0, len = this.config.length; i < len; i++){
5669                 if(includeHidden || !this.isHidden(i)){
5670                     this.totalWidth += this.getColumnWidth(i);
5671                 }
5672             }
5673         }
5674         return this.totalWidth;
5675     },
5676
5677     /**
5678      * Returns the header for the specified column.
5679      * @param {Number} col The column index
5680      * @return {String}
5681      */
5682     getColumnHeader : function(col){
5683         return this.config[col].header;
5684     },
5685
5686     /**
5687      * Sets the header for a column.
5688      * @param {Number} col The column index
5689      * @param {String} header The new header
5690      */
5691     setColumnHeader : function(col, header){
5692         this.config[col].header = header;
5693         this.fireEvent("headerchange", this, col, header);
5694     },
5695
5696     /**
5697      * Returns the tooltip for the specified column.
5698      * @param {Number} col The column index
5699      * @return {String}
5700      */
5701     getColumnTooltip : function(col){
5702             return this.config[col].tooltip;
5703     },
5704     /**
5705      * Sets the tooltip for a column.
5706      * @param {Number} col The column index
5707      * @param {String} tooltip The new tooltip
5708      */
5709     setColumnTooltip : function(col, tooltip){
5710             this.config[col].tooltip = tooltip;
5711     },
5712
5713     /**
5714      * Returns the dataIndex for the specified column.
5715      * @param {Number} col The column index
5716      * @return {Number}
5717      */
5718     getDataIndex : function(col){
5719         return this.config[col].dataIndex;
5720     },
5721
5722     /**
5723      * Sets the dataIndex for a column.
5724      * @param {Number} col The column index
5725      * @param {Number} dataIndex The new dataIndex
5726      */
5727     setDataIndex : function(col, dataIndex){
5728         this.config[col].dataIndex = dataIndex;
5729     },
5730
5731     
5732     
5733     /**
5734      * Returns true if the cell is editable.
5735      * @param {Number} colIndex The column index
5736      * @param {Number} rowIndex The row index - this is nto actually used..?
5737      * @return {Boolean}
5738      */
5739     isCellEditable : function(colIndex, rowIndex){
5740         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5741     },
5742
5743     /**
5744      * Returns the editor defined for the cell/column.
5745      * return false or null to disable editing.
5746      * @param {Number} colIndex The column index
5747      * @param {Number} rowIndex The row index
5748      * @return {Object}
5749      */
5750     getCellEditor : function(colIndex, rowIndex){
5751         return this.config[colIndex].editor;
5752     },
5753
5754     /**
5755      * Sets if a column is editable.
5756      * @param {Number} col The column index
5757      * @param {Boolean} editable True if the column is editable
5758      */
5759     setEditable : function(col, editable){
5760         this.config[col].editable = editable;
5761     },
5762
5763
5764     /**
5765      * Returns true if the column is hidden.
5766      * @param {Number} colIndex The column index
5767      * @return {Boolean}
5768      */
5769     isHidden : function(colIndex){
5770         return this.config[colIndex].hidden;
5771     },
5772
5773
5774     /**
5775      * Returns true if the column width cannot be changed
5776      */
5777     isFixed : function(colIndex){
5778         return this.config[colIndex].fixed;
5779     },
5780
5781     /**
5782      * Returns true if the column can be resized
5783      * @return {Boolean}
5784      */
5785     isResizable : function(colIndex){
5786         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5787     },
5788     /**
5789      * Sets if a column is hidden.
5790      * @param {Number} colIndex The column index
5791      * @param {Boolean} hidden True if the column is hidden
5792      */
5793     setHidden : function(colIndex, hidden){
5794         this.config[colIndex].hidden = hidden;
5795         this.totalWidth = null;
5796         this.fireEvent("hiddenchange", this, colIndex, hidden);
5797     },
5798
5799     /**
5800      * Sets the editor for a column.
5801      * @param {Number} col The column index
5802      * @param {Object} editor The editor object
5803      */
5804     setEditor : function(col, editor){
5805         this.config[col].editor = editor;
5806     }
5807 });
5808
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5810 {
5811     if(typeof value == "object") {
5812         return value;
5813     }
5814         if(typeof value == "string" && value.length < 1){
5815             return "&#160;";
5816         }
5817     
5818         return String.format("{0}", value);
5819 };
5820
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5823 /*
5824  * Based on:
5825  * Ext JS Library 1.1.1
5826  * Copyright(c) 2006-2007, Ext JS, LLC.
5827  *
5828  * Originally Released Under LGPL - original licence link has changed is not relivant.
5829  *
5830  * Fork - LGPL
5831  * <script type="text/javascript">
5832  */
5833  
5834 /**
5835  * @class Roo.LoadMask
5836  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5837  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5839  * element's UpdateManager load indicator and will be destroyed after the initial load.
5840  * @constructor
5841  * Create a new LoadMask
5842  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843  * @param {Object} config The config object
5844  */
5845 Roo.LoadMask = function(el, config){
5846     this.el = Roo.get(el);
5847     Roo.apply(this, config);
5848     if(this.store){
5849         this.store.on('beforeload', this.onBeforeLoad, this);
5850         this.store.on('load', this.onLoad, this);
5851         this.store.on('loadexception', this.onLoadException, this);
5852         this.removeMask = false;
5853     }else{
5854         var um = this.el.getUpdateManager();
5855         um.showLoadIndicator = false; // disable the default indicator
5856         um.on('beforeupdate', this.onBeforeLoad, this);
5857         um.on('update', this.onLoad, this);
5858         um.on('failure', this.onLoad, this);
5859         this.removeMask = true;
5860     }
5861 };
5862
5863 Roo.LoadMask.prototype = {
5864     /**
5865      * @cfg {Boolean} removeMask
5866      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5868      */
5869     /**
5870      * @cfg {String} msg
5871      * The text to display in a centered loading message box (defaults to 'Loading...')
5872      */
5873     msg : 'Loading...',
5874     /**
5875      * @cfg {String} msgCls
5876      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5877      */
5878     msgCls : 'x-mask-loading',
5879
5880     /**
5881      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5882      * @type Boolean
5883      */
5884     disabled: false,
5885
5886     /**
5887      * Disables the mask to prevent it from being displayed
5888      */
5889     disable : function(){
5890        this.disabled = true;
5891     },
5892
5893     /**
5894      * Enables the mask so that it can be displayed
5895      */
5896     enable : function(){
5897         this.disabled = false;
5898     },
5899     
5900     onLoadException : function()
5901     {
5902         Roo.log(arguments);
5903         
5904         if (typeof(arguments[3]) != 'undefined') {
5905             Roo.MessageBox.alert("Error loading",arguments[3]);
5906         } 
5907         /*
5908         try {
5909             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5911             }   
5912         } catch(e) {
5913             
5914         }
5915         */
5916     
5917         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5918     },
5919     // private
5920     onLoad : function()
5921     {
5922         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5923     },
5924
5925     // private
5926     onBeforeLoad : function(){
5927         if(!this.disabled){
5928             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5929         }
5930     },
5931
5932     // private
5933     destroy : function(){
5934         if(this.store){
5935             this.store.un('beforeload', this.onBeforeLoad, this);
5936             this.store.un('load', this.onLoad, this);
5937             this.store.un('loadexception', this.onLoadException, this);
5938         }else{
5939             var um = this.el.getUpdateManager();
5940             um.un('beforeupdate', this.onBeforeLoad, this);
5941             um.un('update', this.onLoad, this);
5942             um.un('failure', this.onLoad, this);
5943         }
5944     }
5945 };/*
5946  * - LGPL
5947  *
5948  * table
5949  * 
5950  */
5951
5952 /**
5953  * @class Roo.bootstrap.Table
5954  * @extends Roo.bootstrap.Component
5955  * Bootstrap Table class
5956  * @cfg {String} cls table class
5957  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958  * @cfg {String} bgcolor Specifies the background color for a table
5959  * @cfg {Number} border Specifies whether the table cells should have borders or not
5960  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961  * @cfg {Number} cellspacing Specifies the space between cells
5962  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964  * @cfg {String} sortable Specifies that the table should be sortable
5965  * @cfg {String} summary Specifies a summary of the content of a table
5966  * @cfg {Number} width Specifies the width of a table
5967  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5968  * 
5969  * @cfg {boolean} striped Should the rows be alternative striped
5970  * @cfg {boolean} bordered Add borders to the table
5971  * @cfg {boolean} hover Add hover highlighting
5972  * @cfg {boolean} condensed Format condensed
5973  * @cfg {boolean} responsive Format condensed
5974  * @cfg {Boolean} loadMask (true|false) default false
5975  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977  * @cfg {Boolean} rowSelection (true|false) default false
5978  * @cfg {Boolean} cellSelection (true|false) default false
5979  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5981  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5982  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
5983  
5984  * 
5985  * @constructor
5986  * Create a new Table
5987  * @param {Object} config The config object
5988  */
5989
5990 Roo.bootstrap.Table = function(config){
5991     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5992     
5993   
5994     
5995     // BC...
5996     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6000     
6001     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6002     if (this.sm) {
6003         this.sm.grid = this;
6004         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005         this.sm = this.selModel;
6006         this.sm.xmodule = this.xmodule || false;
6007     }
6008     
6009     if (this.cm && typeof(this.cm.config) == 'undefined') {
6010         this.colModel = new Roo.grid.ColumnModel(this.cm);
6011         this.cm = this.colModel;
6012         this.cm.xmodule = this.xmodule || false;
6013     }
6014     if (this.store) {
6015         this.store= Roo.factory(this.store, Roo.data);
6016         this.ds = this.store;
6017         this.ds.xmodule = this.xmodule || false;
6018          
6019     }
6020     if (this.footer && this.store) {
6021         this.footer.dataSource = this.ds;
6022         this.footer = Roo.factory(this.footer);
6023     }
6024     
6025     /** @private */
6026     this.addEvents({
6027         /**
6028          * @event cellclick
6029          * Fires when a cell is clicked
6030          * @param {Roo.bootstrap.Table} this
6031          * @param {Roo.Element} el
6032          * @param {Number} rowIndex
6033          * @param {Number} columnIndex
6034          * @param {Roo.EventObject} e
6035          */
6036         "cellclick" : true,
6037         /**
6038          * @event celldblclick
6039          * Fires when a cell is double clicked
6040          * @param {Roo.bootstrap.Table} this
6041          * @param {Roo.Element} el
6042          * @param {Number} rowIndex
6043          * @param {Number} columnIndex
6044          * @param {Roo.EventObject} e
6045          */
6046         "celldblclick" : true,
6047         /**
6048          * @event rowclick
6049          * Fires when a row is clicked
6050          * @param {Roo.bootstrap.Table} this
6051          * @param {Roo.Element} el
6052          * @param {Number} rowIndex
6053          * @param {Roo.EventObject} e
6054          */
6055         "rowclick" : true,
6056         /**
6057          * @event rowdblclick
6058          * Fires when a row is double clicked
6059          * @param {Roo.bootstrap.Table} this
6060          * @param {Roo.Element} el
6061          * @param {Number} rowIndex
6062          * @param {Roo.EventObject} e
6063          */
6064         "rowdblclick" : true,
6065         /**
6066          * @event mouseover
6067          * Fires when a mouseover occur
6068          * @param {Roo.bootstrap.Table} this
6069          * @param {Roo.Element} el
6070          * @param {Number} rowIndex
6071          * @param {Number} columnIndex
6072          * @param {Roo.EventObject} e
6073          */
6074         "mouseover" : true,
6075         /**
6076          * @event mouseout
6077          * Fires when a mouseout occur
6078          * @param {Roo.bootstrap.Table} this
6079          * @param {Roo.Element} el
6080          * @param {Number} rowIndex
6081          * @param {Number} columnIndex
6082          * @param {Roo.EventObject} e
6083          */
6084         "mouseout" : true,
6085         /**
6086          * @event rowclass
6087          * Fires when a row is rendered, so you can change add a style to it.
6088          * @param {Roo.bootstrap.Table} this
6089          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6090          */
6091         'rowclass' : true,
6092           /**
6093          * @event rowsrendered
6094          * Fires when all the  rows have been rendered
6095          * @param {Roo.bootstrap.Table} this
6096          */
6097         'rowsrendered' : true,
6098         /**
6099          * @event contextmenu
6100          * The raw contextmenu event for the entire grid.
6101          * @param {Roo.EventObject} e
6102          */
6103         "contextmenu" : true,
6104         /**
6105          * @event rowcontextmenu
6106          * Fires when a row is right clicked
6107          * @param {Roo.bootstrap.Table} this
6108          * @param {Number} rowIndex
6109          * @param {Roo.EventObject} e
6110          */
6111         "rowcontextmenu" : true,
6112         /**
6113          * @event cellcontextmenu
6114          * Fires when a cell is right clicked
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Number} rowIndex
6117          * @param {Number} cellIndex
6118          * @param {Roo.EventObject} e
6119          */
6120          "cellcontextmenu" : true,
6121          /**
6122          * @event headercontextmenu
6123          * Fires when a header is right clicked
6124          * @param {Roo.bootstrap.Table} this
6125          * @param {Number} columnIndex
6126          * @param {Roo.EventObject} e
6127          */
6128         "headercontextmenu" : true
6129     });
6130 };
6131
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6133     
6134     cls: false,
6135     align: false,
6136     bgcolor: false,
6137     border: false,
6138     cellpadding: false,
6139     cellspacing: false,
6140     frame: false,
6141     rules: false,
6142     sortable: false,
6143     summary: false,
6144     width: false,
6145     striped : false,
6146     scrollBody : false,
6147     bordered: false,
6148     hover:  false,
6149     condensed : false,
6150     responsive : false,
6151     sm : false,
6152     cm : false,
6153     store : false,
6154     loadMask : false,
6155     footerShow : true,
6156     headerShow : true,
6157   
6158     rowSelection : false,
6159     cellSelection : false,
6160     layout : false,
6161     
6162     // Roo.Element - the tbody
6163     mainBody: false,
6164     // Roo.Element - thead element
6165     mainHead: false,
6166     
6167     container: false, // used by gridpanel...
6168     
6169     lazyLoad : false,
6170     
6171     CSS : Roo.util.CSS,
6172     
6173     auto_hide_footer : false,
6174     
6175     getAutoCreate : function()
6176     {
6177         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6178         
6179         cfg = {
6180             tag: 'table',
6181             cls : 'table',
6182             cn : []
6183         };
6184         if (this.scrollBody) {
6185             cfg.cls += ' table-body-fixed';
6186         }    
6187         if (this.striped) {
6188             cfg.cls += ' table-striped';
6189         }
6190         
6191         if (this.hover) {
6192             cfg.cls += ' table-hover';
6193         }
6194         if (this.bordered) {
6195             cfg.cls += ' table-bordered';
6196         }
6197         if (this.condensed) {
6198             cfg.cls += ' table-condensed';
6199         }
6200         if (this.responsive) {
6201             cfg.cls += ' table-responsive';
6202         }
6203         
6204         if (this.cls) {
6205             cfg.cls+=  ' ' +this.cls;
6206         }
6207         
6208         // this lot should be simplifed...
6209         var _t = this;
6210         var cp = [
6211             'align',
6212             'bgcolor',
6213             'border',
6214             'cellpadding',
6215             'cellspacing',
6216             'frame',
6217             'rules',
6218             'sortable',
6219             'summary',
6220             'width'
6221         ].forEach(function(k) {
6222             if (_t[k]) {
6223                 cfg[k] = _t[k];
6224             }
6225         });
6226         
6227         
6228         if (this.layout) {
6229             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6230         }
6231         
6232         if(this.store || this.cm){
6233             if(this.headerShow){
6234                 cfg.cn.push(this.renderHeader());
6235             }
6236             
6237             cfg.cn.push(this.renderBody());
6238             
6239             if(this.footerShow){
6240                 cfg.cn.push(this.renderFooter());
6241             }
6242             // where does this come from?
6243             //cfg.cls+=  ' TableGrid';
6244         }
6245         
6246         return { cn : [ cfg ] };
6247     },
6248     
6249     initEvents : function()
6250     {   
6251         if(!this.store || !this.cm){
6252             return;
6253         }
6254         if (this.selModel) {
6255             this.selModel.initEvents();
6256         }
6257         
6258         
6259         //Roo.log('initEvents with ds!!!!');
6260         
6261         this.mainBody = this.el.select('tbody', true).first();
6262         this.mainHead = this.el.select('thead', true).first();
6263         this.mainFoot = this.el.select('tfoot', true).first();
6264         
6265         
6266         
6267         var _this = this;
6268         
6269         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270             e.on('click', _this.sort, _this);
6271         });
6272         
6273         this.mainBody.on("click", this.onClick, this);
6274         this.mainBody.on("dblclick", this.onDblClick, this);
6275         
6276         // why is this done????? = it breaks dialogs??
6277         //this.parent().el.setStyle('position', 'relative');
6278         
6279         
6280         if (this.footer) {
6281             this.footer.parentId = this.id;
6282             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6283             
6284             if(this.lazyLoad){
6285                 this.el.select('tfoot tr td').first().addClass('hide');
6286             }
6287         } 
6288         
6289         if(this.loadMask) {
6290             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6291         }
6292         
6293         this.store.on('load', this.onLoad, this);
6294         this.store.on('beforeload', this.onBeforeLoad, this);
6295         this.store.on('update', this.onUpdate, this);
6296         this.store.on('add', this.onAdd, this);
6297         this.store.on("clear", this.clear, this);
6298         
6299         this.el.on("contextmenu", this.onContextMenu, this);
6300         
6301         this.mainBody.on('scroll', this.onBodyScroll, this);
6302         
6303         this.cm.on("headerchange", this.onHeaderChange, this);
6304         
6305         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6306         
6307     },
6308     
6309     onContextMenu : function(e, t)
6310     {
6311         this.processEvent("contextmenu", e);
6312     },
6313     
6314     processEvent : function(name, e)
6315     {
6316         if (name != 'touchstart' ) {
6317             this.fireEvent(name, e);    
6318         }
6319         
6320         var t = e.getTarget();
6321         
6322         var cell = Roo.get(t);
6323         
6324         if(!cell){
6325             return;
6326         }
6327         
6328         if(cell.findParent('tfoot', false, true)){
6329             return;
6330         }
6331         
6332         if(cell.findParent('thead', false, true)){
6333             
6334             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335                 cell = Roo.get(t).findParent('th', false, true);
6336                 if (!cell) {
6337                     Roo.log("failed to find th in thead?");
6338                     Roo.log(e.getTarget());
6339                     return;
6340                 }
6341             }
6342             
6343             var cellIndex = cell.dom.cellIndex;
6344             
6345             var ename = name == 'touchstart' ? 'click' : name;
6346             this.fireEvent("header" + ename, this, cellIndex, e);
6347             
6348             return;
6349         }
6350         
6351         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352             cell = Roo.get(t).findParent('td', false, true);
6353             if (!cell) {
6354                 Roo.log("failed to find th in tbody?");
6355                 Roo.log(e.getTarget());
6356                 return;
6357             }
6358         }
6359         
6360         var row = cell.findParent('tr', false, true);
6361         var cellIndex = cell.dom.cellIndex;
6362         var rowIndex = row.dom.rowIndex - 1;
6363         
6364         if(row !== false){
6365             
6366             this.fireEvent("row" + name, this, rowIndex, e);
6367             
6368             if(cell !== false){
6369             
6370                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6371             }
6372         }
6373         
6374     },
6375     
6376     onMouseover : function(e, el)
6377     {
6378         var cell = Roo.get(el);
6379         
6380         if(!cell){
6381             return;
6382         }
6383         
6384         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385             cell = cell.findParent('td', false, true);
6386         }
6387         
6388         var row = cell.findParent('tr', false, true);
6389         var cellIndex = cell.dom.cellIndex;
6390         var rowIndex = row.dom.rowIndex - 1; // start from 0
6391         
6392         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6393         
6394     },
6395     
6396     onMouseout : function(e, el)
6397     {
6398         var cell = Roo.get(el);
6399         
6400         if(!cell){
6401             return;
6402         }
6403         
6404         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405             cell = cell.findParent('td', false, true);
6406         }
6407         
6408         var row = cell.findParent('tr', false, true);
6409         var cellIndex = cell.dom.cellIndex;
6410         var rowIndex = row.dom.rowIndex - 1; // start from 0
6411         
6412         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6413         
6414     },
6415     
6416     onClick : function(e, el)
6417     {
6418         var cell = Roo.get(el);
6419         
6420         if(!cell || (!this.cellSelection && !this.rowSelection)){
6421             return;
6422         }
6423         
6424         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425             cell = cell.findParent('td', false, true);
6426         }
6427         
6428         if(!cell || typeof(cell) == 'undefined'){
6429             return;
6430         }
6431         
6432         var row = cell.findParent('tr', false, true);
6433         
6434         if(!row || typeof(row) == 'undefined'){
6435             return;
6436         }
6437         
6438         var cellIndex = cell.dom.cellIndex;
6439         var rowIndex = this.getRowIndex(row);
6440         
6441         // why??? - should these not be based on SelectionModel?
6442         if(this.cellSelection){
6443             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6444         }
6445         
6446         if(this.rowSelection){
6447             this.fireEvent('rowclick', this, row, rowIndex, e);
6448         }
6449         
6450         
6451     },
6452         
6453     onDblClick : function(e,el)
6454     {
6455         var cell = Roo.get(el);
6456         
6457         if(!cell || (!this.cellSelection && !this.rowSelection)){
6458             return;
6459         }
6460         
6461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462             cell = cell.findParent('td', false, true);
6463         }
6464         
6465         if(!cell || typeof(cell) == 'undefined'){
6466             return;
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         
6471         if(!row || typeof(row) == 'undefined'){
6472             return;
6473         }
6474         
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = this.getRowIndex(row);
6477         
6478         if(this.cellSelection){
6479             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6480         }
6481         
6482         if(this.rowSelection){
6483             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6484         }
6485     },
6486     
6487     sort : function(e,el)
6488     {
6489         var col = Roo.get(el);
6490         
6491         if(!col.hasClass('sortable')){
6492             return;
6493         }
6494         
6495         var sort = col.attr('sort');
6496         var dir = 'ASC';
6497         
6498         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6499             dir = 'DESC';
6500         }
6501         
6502         this.store.sortInfo = {field : sort, direction : dir};
6503         
6504         if (this.footer) {
6505             Roo.log("calling footer first");
6506             this.footer.onClick('first');
6507         } else {
6508         
6509             this.store.load({ params : { start : 0 } });
6510         }
6511     },
6512     
6513     renderHeader : function()
6514     {
6515         var header = {
6516             tag: 'thead',
6517             cn : []
6518         };
6519         
6520         var cm = this.cm;
6521         this.totalWidth = 0;
6522         
6523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6524             
6525             var config = cm.config[i];
6526             
6527             var c = {
6528                 tag: 'th',
6529                 cls : 'x-hcol-' + i,
6530                 style : '',
6531                 html: cm.getColumnHeader(i)
6532             };
6533             
6534             var hh = '';
6535             
6536             if(typeof(config.sortable) != 'undefined' && config.sortable){
6537                 c.cls = 'sortable';
6538                 c.html = '<i class="glyphicon"></i>' + c.html;
6539             }
6540             
6541             if(typeof(config.lgHeader) != 'undefined'){
6542                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6543             }
6544             
6545             if(typeof(config.mdHeader) != 'undefined'){
6546                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6547             }
6548             
6549             if(typeof(config.smHeader) != 'undefined'){
6550                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6551             }
6552             
6553             if(typeof(config.xsHeader) != 'undefined'){
6554                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6555             }
6556             
6557             if(hh.length){
6558                 c.html = hh;
6559             }
6560             
6561             if(typeof(config.tooltip) != 'undefined'){
6562                 c.tooltip = config.tooltip;
6563             }
6564             
6565             if(typeof(config.colspan) != 'undefined'){
6566                 c.colspan = config.colspan;
6567             }
6568             
6569             if(typeof(config.hidden) != 'undefined' && config.hidden){
6570                 c.style += ' display:none;';
6571             }
6572             
6573             if(typeof(config.dataIndex) != 'undefined'){
6574                 c.sort = config.dataIndex;
6575             }
6576             
6577            
6578             
6579             if(typeof(config.align) != 'undefined' && config.align.length){
6580                 c.style += ' text-align:' + config.align + ';';
6581             }
6582             
6583             if(typeof(config.width) != 'undefined'){
6584                 c.style += ' width:' + config.width + 'px;';
6585                 this.totalWidth += config.width;
6586             } else {
6587                 this.totalWidth += 100; // assume minimum of 100 per column?
6588             }
6589             
6590             if(typeof(config.cls) != 'undefined'){
6591                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6592             }
6593             
6594             ['xs','sm','md','lg'].map(function(size){
6595                 
6596                 if(typeof(config[size]) == 'undefined'){
6597                     return;
6598                 }
6599                 
6600                 if (!config[size]) { // 0 = hidden
6601                     c.cls += ' hidden-' + size;
6602                     return;
6603                 }
6604                 
6605                 c.cls += ' col-' + size + '-' + config[size];
6606
6607             });
6608             
6609             header.cn.push(c)
6610         }
6611         
6612         return header;
6613     },
6614     
6615     renderBody : function()
6616     {
6617         var body = {
6618             tag: 'tbody',
6619             cn : [
6620                 {
6621                     tag: 'tr',
6622                     cn : [
6623                         {
6624                             tag : 'td',
6625                             colspan :  this.cm.getColumnCount()
6626                         }
6627                     ]
6628                 }
6629             ]
6630         };
6631         
6632         return body;
6633     },
6634     
6635     renderFooter : function()
6636     {
6637         var footer = {
6638             tag: 'tfoot',
6639             cn : [
6640                 {
6641                     tag: 'tr',
6642                     cn : [
6643                         {
6644                             tag : 'td',
6645                             colspan :  this.cm.getColumnCount()
6646                         }
6647                     ]
6648                 }
6649             ]
6650         };
6651         
6652         return footer;
6653     },
6654     
6655     
6656     
6657     onLoad : function()
6658     {
6659 //        Roo.log('ds onload');
6660         this.clear();
6661         
6662         var _this = this;
6663         var cm = this.cm;
6664         var ds = this.store;
6665         
6666         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668             if (_this.store.sortInfo) {
6669                     
6670                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6672                 }
6673                 
6674                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6676                 }
6677             }
6678         });
6679         
6680         var tbody =  this.mainBody;
6681               
6682         if(ds.getCount() > 0){
6683             ds.data.each(function(d,rowIndex){
6684                 var row =  this.renderRow(cm, ds, rowIndex);
6685                 
6686                 tbody.createChild(row);
6687                 
6688                 var _this = this;
6689                 
6690                 if(row.cellObjects.length){
6691                     Roo.each(row.cellObjects, function(r){
6692                         _this.renderCellObject(r);
6693                     })
6694                 }
6695                 
6696             }, this);
6697         }
6698         
6699         var tfoot = this.el.select('tfoot', true).first();
6700         
6701         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6702             
6703             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6704             
6705             var total = this.ds.getTotalCount();
6706             
6707             if(this.footer.pageSize < total){
6708                 this.mainFoot.show();
6709             }
6710         }
6711         
6712         Roo.each(this.el.select('tbody td', true).elements, function(e){
6713             e.on('mouseover', _this.onMouseover, _this);
6714         });
6715         
6716         Roo.each(this.el.select('tbody td', true).elements, function(e){
6717             e.on('mouseout', _this.onMouseout, _this);
6718         });
6719         this.fireEvent('rowsrendered', this);
6720         
6721         this.autoSize();
6722     },
6723     
6724     
6725     onUpdate : function(ds,record)
6726     {
6727         this.refreshRow(record);
6728         this.autoSize();
6729     },
6730     
6731     onRemove : function(ds, record, index, isUpdate){
6732         if(isUpdate !== true){
6733             this.fireEvent("beforerowremoved", this, index, record);
6734         }
6735         var bt = this.mainBody.dom;
6736         
6737         var rows = this.el.select('tbody > tr', true).elements;
6738         
6739         if(typeof(rows[index]) != 'undefined'){
6740             bt.removeChild(rows[index].dom);
6741         }
6742         
6743 //        if(bt.rows[index]){
6744 //            bt.removeChild(bt.rows[index]);
6745 //        }
6746         
6747         if(isUpdate !== true){
6748             //this.stripeRows(index);
6749             //this.syncRowHeights(index, index);
6750             //this.layout();
6751             this.fireEvent("rowremoved", this, index, record);
6752         }
6753     },
6754     
6755     onAdd : function(ds, records, rowIndex)
6756     {
6757         //Roo.log('on Add called');
6758         // - note this does not handle multiple adding very well..
6759         var bt = this.mainBody.dom;
6760         for (var i =0 ; i < records.length;i++) {
6761             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762             //Roo.log(records[i]);
6763             //Roo.log(this.store.getAt(rowIndex+i));
6764             this.insertRow(this.store, rowIndex + i, false);
6765             return;
6766         }
6767         
6768     },
6769     
6770     
6771     refreshRow : function(record){
6772         var ds = this.store, index;
6773         if(typeof record == 'number'){
6774             index = record;
6775             record = ds.getAt(index);
6776         }else{
6777             index = ds.indexOf(record);
6778         }
6779         this.insertRow(ds, index, true);
6780         this.autoSize();
6781         this.onRemove(ds, record, index+1, true);
6782         this.autoSize();
6783         //this.syncRowHeights(index, index);
6784         //this.layout();
6785         this.fireEvent("rowupdated", this, index, record);
6786     },
6787     
6788     insertRow : function(dm, rowIndex, isUpdate){
6789         
6790         if(!isUpdate){
6791             this.fireEvent("beforerowsinserted", this, rowIndex);
6792         }
6793             //var s = this.getScrollState();
6794         var row = this.renderRow(this.cm, this.store, rowIndex);
6795         // insert before rowIndex..
6796         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6797         
6798         var _this = this;
6799                 
6800         if(row.cellObjects.length){
6801             Roo.each(row.cellObjects, function(r){
6802                 _this.renderCellObject(r);
6803             })
6804         }
6805             
6806         if(!isUpdate){
6807             this.fireEvent("rowsinserted", this, rowIndex);
6808             //this.syncRowHeights(firstRow, lastRow);
6809             //this.stripeRows(firstRow);
6810             //this.layout();
6811         }
6812         
6813     },
6814     
6815     
6816     getRowDom : function(rowIndex)
6817     {
6818         var rows = this.el.select('tbody > tr', true).elements;
6819         
6820         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6821         
6822     },
6823     // returns the object tree for a tr..
6824   
6825     
6826     renderRow : function(cm, ds, rowIndex) 
6827     {
6828         var d = ds.getAt(rowIndex);
6829         
6830         var row = {
6831             tag : 'tr',
6832             cls : 'x-row-' + rowIndex,
6833             cn : []
6834         };
6835             
6836         var cellObjects = [];
6837         
6838         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839             var config = cm.config[i];
6840             
6841             var renderer = cm.getRenderer(i);
6842             var value = '';
6843             var id = false;
6844             
6845             if(typeof(renderer) !== 'undefined'){
6846                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6847             }
6848             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849             // and are rendered into the cells after the row is rendered - using the id for the element.
6850             
6851             if(typeof(value) === 'object'){
6852                 id = Roo.id();
6853                 cellObjects.push({
6854                     container : id,
6855                     cfg : value 
6856                 })
6857             }
6858             
6859             var rowcfg = {
6860                 record: d,
6861                 rowIndex : rowIndex,
6862                 colIndex : i,
6863                 rowClass : ''
6864             };
6865
6866             this.fireEvent('rowclass', this, rowcfg);
6867             
6868             var td = {
6869                 tag: 'td',
6870                 cls : rowcfg.rowClass + ' x-col-' + i,
6871                 style: '',
6872                 html: (typeof(value) === 'object') ? '' : value
6873             };
6874             
6875             if (id) {
6876                 td.id = id;
6877             }
6878             
6879             if(typeof(config.colspan) != 'undefined'){
6880                 td.colspan = config.colspan;
6881             }
6882             
6883             if(typeof(config.hidden) != 'undefined' && config.hidden){
6884                 td.style += ' display:none;';
6885             }
6886             
6887             if(typeof(config.align) != 'undefined' && config.align.length){
6888                 td.style += ' text-align:' + config.align + ';';
6889             }
6890             if(typeof(config.valign) != 'undefined' && config.valign.length){
6891                 td.style += ' vertical-align:' + config.valign + ';';
6892             }
6893             
6894             if(typeof(config.width) != 'undefined'){
6895                 td.style += ' width:' +  config.width + 'px;';
6896             }
6897             
6898             if(typeof(config.cursor) != 'undefined'){
6899                 td.style += ' cursor:' +  config.cursor + ';';
6900             }
6901             
6902             if(typeof(config.cls) != 'undefined'){
6903                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6904             }
6905             
6906             ['xs','sm','md','lg'].map(function(size){
6907                 
6908                 if(typeof(config[size]) == 'undefined'){
6909                     return;
6910                 }
6911                 
6912                 if (!config[size]) { // 0 = hidden
6913                     td.cls += ' hidden-' + size;
6914                     return;
6915                 }
6916                 
6917                 td.cls += ' col-' + size + '-' + config[size];
6918
6919             });
6920             
6921             row.cn.push(td);
6922            
6923         }
6924         
6925         row.cellObjects = cellObjects;
6926         
6927         return row;
6928           
6929     },
6930     
6931     
6932     
6933     onBeforeLoad : function()
6934     {
6935         
6936     },
6937      /**
6938      * Remove all rows
6939      */
6940     clear : function()
6941     {
6942         this.el.select('tbody', true).first().dom.innerHTML = '';
6943     },
6944     /**
6945      * Show or hide a row.
6946      * @param {Number} rowIndex to show or hide
6947      * @param {Boolean} state hide
6948      */
6949     setRowVisibility : function(rowIndex, state)
6950     {
6951         var bt = this.mainBody.dom;
6952         
6953         var rows = this.el.select('tbody > tr', true).elements;
6954         
6955         if(typeof(rows[rowIndex]) == 'undefined'){
6956             return;
6957         }
6958         rows[rowIndex].dom.style.display = state ? '' : 'none';
6959     },
6960     
6961     
6962     getSelectionModel : function(){
6963         if(!this.selModel){
6964             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6965         }
6966         return this.selModel;
6967     },
6968     /*
6969      * Render the Roo.bootstrap object from renderder
6970      */
6971     renderCellObject : function(r)
6972     {
6973         var _this = this;
6974         
6975         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6976         
6977         var t = r.cfg.render(r.container);
6978         
6979         if(r.cfg.cn){
6980             Roo.each(r.cfg.cn, function(c){
6981                 var child = {
6982                     container: t.getChildContainer(),
6983                     cfg: c
6984                 };
6985                 _this.renderCellObject(child);
6986             })
6987         }
6988     },
6989     
6990     getRowIndex : function(row)
6991     {
6992         var rowIndex = -1;
6993         
6994         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6995             if(el != row){
6996                 return;
6997             }
6998             
6999             rowIndex = index;
7000         });
7001         
7002         return rowIndex;
7003     },
7004      /**
7005      * Returns the grid's underlying element = used by panel.Grid
7006      * @return {Element} The element
7007      */
7008     getGridEl : function(){
7009         return this.el;
7010     },
7011      /**
7012      * Forces a resize - used by panel.Grid
7013      * @return {Element} The element
7014      */
7015     autoSize : function()
7016     {
7017         //var ctr = Roo.get(this.container.dom.parentElement);
7018         var ctr = Roo.get(this.el.dom);
7019         
7020         var thd = this.getGridEl().select('thead',true).first();
7021         var tbd = this.getGridEl().select('tbody', true).first();
7022         var tfd = this.getGridEl().select('tfoot', true).first();
7023         
7024         var cw = ctr.getWidth();
7025         
7026         if (tbd) {
7027             
7028             tbd.setSize(ctr.getWidth(),
7029                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7030             );
7031             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7032             cw -= barsize;
7033         }
7034         cw = Math.max(cw, this.totalWidth);
7035         this.getGridEl().select('tr',true).setWidth(cw);
7036         // resize 'expandable coloumn?
7037         
7038         return; // we doe not have a view in this design..
7039         
7040     },
7041     onBodyScroll: function()
7042     {
7043         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7044         if(this.mainHead){
7045             this.mainHead.setStyle({
7046                 'position' : 'relative',
7047                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7048             });
7049         }
7050         
7051         if(this.lazyLoad){
7052             
7053             var scrollHeight = this.mainBody.dom.scrollHeight;
7054             
7055             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7056             
7057             var height = this.mainBody.getHeight();
7058             
7059             if(scrollHeight - height == scrollTop) {
7060                 
7061                 var total = this.ds.getTotalCount();
7062                 
7063                 if(this.footer.cursor + this.footer.pageSize < total){
7064                     
7065                     this.footer.ds.load({
7066                         params : {
7067                             start : this.footer.cursor + this.footer.pageSize,
7068                             limit : this.footer.pageSize
7069                         },
7070                         add : true
7071                     });
7072                 }
7073             }
7074             
7075         }
7076     },
7077     
7078     onHeaderChange : function()
7079     {
7080         var header = this.renderHeader();
7081         var table = this.el.select('table', true).first();
7082         
7083         this.mainHead.remove();
7084         this.mainHead = table.createChild(header, this.mainBody, false);
7085     },
7086     
7087     onHiddenChange : function(colModel, colIndex, hidden)
7088     {
7089         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7091         
7092         this.CSS.updateRule(thSelector, "display", "");
7093         this.CSS.updateRule(tdSelector, "display", "");
7094         
7095         if(hidden){
7096             this.CSS.updateRule(thSelector, "display", "none");
7097             this.CSS.updateRule(tdSelector, "display", "none");
7098         }
7099         
7100         this.onHeaderChange();
7101         this.onLoad();
7102     },
7103     
7104     setColumnWidth: function(col_index, width)
7105     {
7106         // width = "md-2 xs-2..."
7107         if(!this.colModel.config[col_index]) {
7108             return;
7109         }
7110         
7111         var w = width.split(" ");
7112         
7113         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7114         
7115         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7116         
7117         h_row.classList.replace(
7118             "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7119             "col-"+size_cls[0]+"-"+size_cls[1]
7120         );
7121         
7122         this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7123         
7124         for(var i = 0; i < rows.length; i++) {
7125             
7126             for(var j = 0; w.length; j++) {
7127                 
7128                 var size_cls = w[j].split("-");
7129                 
7130                 if(!Number.isInteger(size_cls[1] * 1)) {
7131                     continue;
7132                 }
7133                 
7134                 if(!this.colModel.config[col_index][size_cls[0]]) {
7135                     continue;
7136                 }
7137                 
7138                 rows[i].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         }
7144     }
7145 });
7146
7147  
7148
7149  /*
7150  * - LGPL
7151  *
7152  * table cell
7153  * 
7154  */
7155
7156 /**
7157  * @class Roo.bootstrap.TableCell
7158  * @extends Roo.bootstrap.Component
7159  * Bootstrap TableCell class
7160  * @cfg {String} html cell contain text
7161  * @cfg {String} cls cell class
7162  * @cfg {String} tag cell tag (td|th) default td
7163  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7164  * @cfg {String} align Aligns the content in a cell
7165  * @cfg {String} axis Categorizes cells
7166  * @cfg {String} bgcolor Specifies the background color of a cell
7167  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7168  * @cfg {Number} colspan Specifies the number of columns a cell should span
7169  * @cfg {String} headers Specifies one or more header cells a cell is related to
7170  * @cfg {Number} height Sets the height of a cell
7171  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7172  * @cfg {Number} rowspan Sets the number of rows a cell should span
7173  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7174  * @cfg {String} valign Vertical aligns the content in a cell
7175  * @cfg {Number} width Specifies the width of a cell
7176  * 
7177  * @constructor
7178  * Create a new TableCell
7179  * @param {Object} config The config object
7180  */
7181
7182 Roo.bootstrap.TableCell = function(config){
7183     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7184 };
7185
7186 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7187     
7188     html: false,
7189     cls: false,
7190     tag: false,
7191     abbr: false,
7192     align: false,
7193     axis: false,
7194     bgcolor: false,
7195     charoff: false,
7196     colspan: false,
7197     headers: false,
7198     height: false,
7199     nowrap: false,
7200     rowspan: false,
7201     scope: false,
7202     valign: false,
7203     width: false,
7204     
7205     
7206     getAutoCreate : function(){
7207         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7208         
7209         cfg = {
7210             tag: 'td'
7211         };
7212         
7213         if(this.tag){
7214             cfg.tag = this.tag;
7215         }
7216         
7217         if (this.html) {
7218             cfg.html=this.html
7219         }
7220         if (this.cls) {
7221             cfg.cls=this.cls
7222         }
7223         if (this.abbr) {
7224             cfg.abbr=this.abbr
7225         }
7226         if (this.align) {
7227             cfg.align=this.align
7228         }
7229         if (this.axis) {
7230             cfg.axis=this.axis
7231         }
7232         if (this.bgcolor) {
7233             cfg.bgcolor=this.bgcolor
7234         }
7235         if (this.charoff) {
7236             cfg.charoff=this.charoff
7237         }
7238         if (this.colspan) {
7239             cfg.colspan=this.colspan
7240         }
7241         if (this.headers) {
7242             cfg.headers=this.headers
7243         }
7244         if (this.height) {
7245             cfg.height=this.height
7246         }
7247         if (this.nowrap) {
7248             cfg.nowrap=this.nowrap
7249         }
7250         if (this.rowspan) {
7251             cfg.rowspan=this.rowspan
7252         }
7253         if (this.scope) {
7254             cfg.scope=this.scope
7255         }
7256         if (this.valign) {
7257             cfg.valign=this.valign
7258         }
7259         if (this.width) {
7260             cfg.width=this.width
7261         }
7262         
7263         
7264         return cfg;
7265     }
7266    
7267 });
7268
7269  
7270
7271  /*
7272  * - LGPL
7273  *
7274  * table row
7275  * 
7276  */
7277
7278 /**
7279  * @class Roo.bootstrap.TableRow
7280  * @extends Roo.bootstrap.Component
7281  * Bootstrap TableRow class
7282  * @cfg {String} cls row class
7283  * @cfg {String} align Aligns the content in a table row
7284  * @cfg {String} bgcolor Specifies a background color for a table row
7285  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7286  * @cfg {String} valign Vertical aligns the content in a table row
7287  * 
7288  * @constructor
7289  * Create a new TableRow
7290  * @param {Object} config The config object
7291  */
7292
7293 Roo.bootstrap.TableRow = function(config){
7294     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7295 };
7296
7297 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7298     
7299     cls: false,
7300     align: false,
7301     bgcolor: false,
7302     charoff: false,
7303     valign: false,
7304     
7305     getAutoCreate : function(){
7306         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7307         
7308         cfg = {
7309             tag: 'tr'
7310         };
7311             
7312         if(this.cls){
7313             cfg.cls = this.cls;
7314         }
7315         if(this.align){
7316             cfg.align = this.align;
7317         }
7318         if(this.bgcolor){
7319             cfg.bgcolor = this.bgcolor;
7320         }
7321         if(this.charoff){
7322             cfg.charoff = this.charoff;
7323         }
7324         if(this.valign){
7325             cfg.valign = this.valign;
7326         }
7327         
7328         return cfg;
7329     }
7330    
7331 });
7332
7333  
7334
7335  /*
7336  * - LGPL
7337  *
7338  * table body
7339  * 
7340  */
7341
7342 /**
7343  * @class Roo.bootstrap.TableBody
7344  * @extends Roo.bootstrap.Component
7345  * Bootstrap TableBody class
7346  * @cfg {String} cls element class
7347  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7348  * @cfg {String} align Aligns the content inside the element
7349  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7350  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7351  * 
7352  * @constructor
7353  * Create a new TableBody
7354  * @param {Object} config The config object
7355  */
7356
7357 Roo.bootstrap.TableBody = function(config){
7358     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7359 };
7360
7361 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7362     
7363     cls: false,
7364     tag: false,
7365     align: false,
7366     charoff: false,
7367     valign: false,
7368     
7369     getAutoCreate : function(){
7370         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7371         
7372         cfg = {
7373             tag: 'tbody'
7374         };
7375             
7376         if (this.cls) {
7377             cfg.cls=this.cls
7378         }
7379         if(this.tag){
7380             cfg.tag = this.tag;
7381         }
7382         
7383         if(this.align){
7384             cfg.align = this.align;
7385         }
7386         if(this.charoff){
7387             cfg.charoff = this.charoff;
7388         }
7389         if(this.valign){
7390             cfg.valign = this.valign;
7391         }
7392         
7393         return cfg;
7394     }
7395     
7396     
7397 //    initEvents : function()
7398 //    {
7399 //        
7400 //        if(!this.store){
7401 //            return;
7402 //        }
7403 //        
7404 //        this.store = Roo.factory(this.store, Roo.data);
7405 //        this.store.on('load', this.onLoad, this);
7406 //        
7407 //        this.store.load();
7408 //        
7409 //    },
7410 //    
7411 //    onLoad: function () 
7412 //    {   
7413 //        this.fireEvent('load', this);
7414 //    }
7415 //    
7416 //   
7417 });
7418
7419  
7420
7421  /*
7422  * Based on:
7423  * Ext JS Library 1.1.1
7424  * Copyright(c) 2006-2007, Ext JS, LLC.
7425  *
7426  * Originally Released Under LGPL - original licence link has changed is not relivant.
7427  *
7428  * Fork - LGPL
7429  * <script type="text/javascript">
7430  */
7431
7432 // as we use this in bootstrap.
7433 Roo.namespace('Roo.form');
7434  /**
7435  * @class Roo.form.Action
7436  * Internal Class used to handle form actions
7437  * @constructor
7438  * @param {Roo.form.BasicForm} el The form element or its id
7439  * @param {Object} config Configuration options
7440  */
7441
7442  
7443  
7444 // define the action interface
7445 Roo.form.Action = function(form, options){
7446     this.form = form;
7447     this.options = options || {};
7448 };
7449 /**
7450  * Client Validation Failed
7451  * @const 
7452  */
7453 Roo.form.Action.CLIENT_INVALID = 'client';
7454 /**
7455  * Server Validation Failed
7456  * @const 
7457  */
7458 Roo.form.Action.SERVER_INVALID = 'server';
7459  /**
7460  * Connect to Server Failed
7461  * @const 
7462  */
7463 Roo.form.Action.CONNECT_FAILURE = 'connect';
7464 /**
7465  * Reading Data from Server Failed
7466  * @const 
7467  */
7468 Roo.form.Action.LOAD_FAILURE = 'load';
7469
7470 Roo.form.Action.prototype = {
7471     type : 'default',
7472     failureType : undefined,
7473     response : undefined,
7474     result : undefined,
7475
7476     // interface method
7477     run : function(options){
7478
7479     },
7480
7481     // interface method
7482     success : function(response){
7483
7484     },
7485
7486     // interface method
7487     handleResponse : function(response){
7488
7489     },
7490
7491     // default connection failure
7492     failure : function(response){
7493         
7494         this.response = response;
7495         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7496         this.form.afterAction(this, false);
7497     },
7498
7499     processResponse : function(response){
7500         this.response = response;
7501         if(!response.responseText){
7502             return true;
7503         }
7504         this.result = this.handleResponse(response);
7505         return this.result;
7506     },
7507
7508     // utility functions used internally
7509     getUrl : function(appendParams){
7510         var url = this.options.url || this.form.url || this.form.el.dom.action;
7511         if(appendParams){
7512             var p = this.getParams();
7513             if(p){
7514                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7515             }
7516         }
7517         return url;
7518     },
7519
7520     getMethod : function(){
7521         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7522     },
7523
7524     getParams : function(){
7525         var bp = this.form.baseParams;
7526         var p = this.options.params;
7527         if(p){
7528             if(typeof p == "object"){
7529                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7530             }else if(typeof p == 'string' && bp){
7531                 p += '&' + Roo.urlEncode(bp);
7532             }
7533         }else if(bp){
7534             p = Roo.urlEncode(bp);
7535         }
7536         return p;
7537     },
7538
7539     createCallback : function(){
7540         return {
7541             success: this.success,
7542             failure: this.failure,
7543             scope: this,
7544             timeout: (this.form.timeout*1000),
7545             upload: this.form.fileUpload ? this.success : undefined
7546         };
7547     }
7548 };
7549
7550 Roo.form.Action.Submit = function(form, options){
7551     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7552 };
7553
7554 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7555     type : 'submit',
7556
7557     haveProgress : false,
7558     uploadComplete : false,
7559     
7560     // uploadProgress indicator.
7561     uploadProgress : function()
7562     {
7563         if (!this.form.progressUrl) {
7564             return;
7565         }
7566         
7567         if (!this.haveProgress) {
7568             Roo.MessageBox.progress("Uploading", "Uploading");
7569         }
7570         if (this.uploadComplete) {
7571            Roo.MessageBox.hide();
7572            return;
7573         }
7574         
7575         this.haveProgress = true;
7576    
7577         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7578         
7579         var c = new Roo.data.Connection();
7580         c.request({
7581             url : this.form.progressUrl,
7582             params: {
7583                 id : uid
7584             },
7585             method: 'GET',
7586             success : function(req){
7587                //console.log(data);
7588                 var rdata = false;
7589                 var edata;
7590                 try  {
7591                    rdata = Roo.decode(req.responseText)
7592                 } catch (e) {
7593                     Roo.log("Invalid data from server..");
7594                     Roo.log(edata);
7595                     return;
7596                 }
7597                 if (!rdata || !rdata.success) {
7598                     Roo.log(rdata);
7599                     Roo.MessageBox.alert(Roo.encode(rdata));
7600                     return;
7601                 }
7602                 var data = rdata.data;
7603                 
7604                 if (this.uploadComplete) {
7605                    Roo.MessageBox.hide();
7606                    return;
7607                 }
7608                    
7609                 if (data){
7610                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7611                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7612                     );
7613                 }
7614                 this.uploadProgress.defer(2000,this);
7615             },
7616        
7617             failure: function(data) {
7618                 Roo.log('progress url failed ');
7619                 Roo.log(data);
7620             },
7621             scope : this
7622         });
7623            
7624     },
7625     
7626     
7627     run : function()
7628     {
7629         // run get Values on the form, so it syncs any secondary forms.
7630         this.form.getValues();
7631         
7632         var o = this.options;
7633         var method = this.getMethod();
7634         var isPost = method == 'POST';
7635         if(o.clientValidation === false || this.form.isValid()){
7636             
7637             if (this.form.progressUrl) {
7638                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7639                     (new Date() * 1) + '' + Math.random());
7640                     
7641             } 
7642             
7643             
7644             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7645                 form:this.form.el.dom,
7646                 url:this.getUrl(!isPost),
7647                 method: method,
7648                 params:isPost ? this.getParams() : null,
7649                 isUpload: this.form.fileUpload
7650             }));
7651             
7652             this.uploadProgress();
7653
7654         }else if (o.clientValidation !== false){ // client validation failed
7655             this.failureType = Roo.form.Action.CLIENT_INVALID;
7656             this.form.afterAction(this, false);
7657         }
7658     },
7659
7660     success : function(response)
7661     {
7662         this.uploadComplete= true;
7663         if (this.haveProgress) {
7664             Roo.MessageBox.hide();
7665         }
7666         
7667         
7668         var result = this.processResponse(response);
7669         if(result === true || result.success){
7670             this.form.afterAction(this, true);
7671             return;
7672         }
7673         if(result.errors){
7674             this.form.markInvalid(result.errors);
7675             this.failureType = Roo.form.Action.SERVER_INVALID;
7676         }
7677         this.form.afterAction(this, false);
7678     },
7679     failure : function(response)
7680     {
7681         this.uploadComplete= true;
7682         if (this.haveProgress) {
7683             Roo.MessageBox.hide();
7684         }
7685         
7686         this.response = response;
7687         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7688         this.form.afterAction(this, false);
7689     },
7690     
7691     handleResponse : function(response){
7692         if(this.form.errorReader){
7693             var rs = this.form.errorReader.read(response);
7694             var errors = [];
7695             if(rs.records){
7696                 for(var i = 0, len = rs.records.length; i < len; i++) {
7697                     var r = rs.records[i];
7698                     errors[i] = r.data;
7699                 }
7700             }
7701             if(errors.length < 1){
7702                 errors = null;
7703             }
7704             return {
7705                 success : rs.success,
7706                 errors : errors
7707             };
7708         }
7709         var ret = false;
7710         try {
7711             ret = Roo.decode(response.responseText);
7712         } catch (e) {
7713             ret = {
7714                 success: false,
7715                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7716                 errors : []
7717             };
7718         }
7719         return ret;
7720         
7721     }
7722 });
7723
7724
7725 Roo.form.Action.Load = function(form, options){
7726     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7727     this.reader = this.form.reader;
7728 };
7729
7730 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7731     type : 'load',
7732
7733     run : function(){
7734         
7735         Roo.Ajax.request(Roo.apply(
7736                 this.createCallback(), {
7737                     method:this.getMethod(),
7738                     url:this.getUrl(false),
7739                     params:this.getParams()
7740         }));
7741     },
7742
7743     success : function(response){
7744         
7745         var result = this.processResponse(response);
7746         if(result === true || !result.success || !result.data){
7747             this.failureType = Roo.form.Action.LOAD_FAILURE;
7748             this.form.afterAction(this, false);
7749             return;
7750         }
7751         this.form.clearInvalid();
7752         this.form.setValues(result.data);
7753         this.form.afterAction(this, true);
7754     },
7755
7756     handleResponse : function(response){
7757         if(this.form.reader){
7758             var rs = this.form.reader.read(response);
7759             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7760             return {
7761                 success : rs.success,
7762                 data : data
7763             };
7764         }
7765         return Roo.decode(response.responseText);
7766     }
7767 });
7768
7769 Roo.form.Action.ACTION_TYPES = {
7770     'load' : Roo.form.Action.Load,
7771     'submit' : Roo.form.Action.Submit
7772 };/*
7773  * - LGPL
7774  *
7775  * form
7776  *
7777  */
7778
7779 /**
7780  * @class Roo.bootstrap.Form
7781  * @extends Roo.bootstrap.Component
7782  * Bootstrap Form class
7783  * @cfg {String} method  GET | POST (default POST)
7784  * @cfg {String} labelAlign top | left (default top)
7785  * @cfg {String} align left  | right - for navbars
7786  * @cfg {Boolean} loadMask load mask when submit (default true)
7787
7788  *
7789  * @constructor
7790  * Create a new Form
7791  * @param {Object} config The config object
7792  */
7793
7794
7795 Roo.bootstrap.Form = function(config){
7796     
7797     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7798     
7799     Roo.bootstrap.Form.popover.apply();
7800     
7801     this.addEvents({
7802         /**
7803          * @event clientvalidation
7804          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7805          * @param {Form} this
7806          * @param {Boolean} valid true if the form has passed client-side validation
7807          */
7808         clientvalidation: true,
7809         /**
7810          * @event beforeaction
7811          * Fires before any action is performed. Return false to cancel the action.
7812          * @param {Form} this
7813          * @param {Action} action The action to be performed
7814          */
7815         beforeaction: true,
7816         /**
7817          * @event actionfailed
7818          * Fires when an action fails.
7819          * @param {Form} this
7820          * @param {Action} action The action that failed
7821          */
7822         actionfailed : true,
7823         /**
7824          * @event actioncomplete
7825          * Fires when an action is completed.
7826          * @param {Form} this
7827          * @param {Action} action The action that completed
7828          */
7829         actioncomplete : true
7830     });
7831 };
7832
7833 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7834
7835      /**
7836      * @cfg {String} method
7837      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7838      */
7839     method : 'POST',
7840     /**
7841      * @cfg {String} url
7842      * The URL to use for form actions if one isn't supplied in the action options.
7843      */
7844     /**
7845      * @cfg {Boolean} fileUpload
7846      * Set to true if this form is a file upload.
7847      */
7848
7849     /**
7850      * @cfg {Object} baseParams
7851      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7852      */
7853
7854     /**
7855      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7856      */
7857     timeout: 30,
7858     /**
7859      * @cfg {Sting} align (left|right) for navbar forms
7860      */
7861     align : 'left',
7862
7863     // private
7864     activeAction : null,
7865
7866     /**
7867      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7868      * element by passing it or its id or mask the form itself by passing in true.
7869      * @type Mixed
7870      */
7871     waitMsgTarget : false,
7872
7873     loadMask : true,
7874     
7875     /**
7876      * @cfg {Boolean} errorMask (true|false) default false
7877      */
7878     errorMask : false,
7879     
7880     /**
7881      * @cfg {Number} maskOffset Default 100
7882      */
7883     maskOffset : 100,
7884     
7885     /**
7886      * @cfg {Boolean} maskBody
7887      */
7888     maskBody : false,
7889
7890     getAutoCreate : function(){
7891
7892         var cfg = {
7893             tag: 'form',
7894             method : this.method || 'POST',
7895             id : this.id || Roo.id(),
7896             cls : ''
7897         };
7898         if (this.parent().xtype.match(/^Nav/)) {
7899             cfg.cls = 'navbar-form navbar-' + this.align;
7900
7901         }
7902
7903         if (this.labelAlign == 'left' ) {
7904             cfg.cls += ' form-horizontal';
7905         }
7906
7907
7908         return cfg;
7909     },
7910     initEvents : function()
7911     {
7912         this.el.on('submit', this.onSubmit, this);
7913         // this was added as random key presses on the form where triggering form submit.
7914         this.el.on('keypress', function(e) {
7915             if (e.getCharCode() != 13) {
7916                 return true;
7917             }
7918             // we might need to allow it for textareas.. and some other items.
7919             // check e.getTarget().
7920
7921             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7922                 return true;
7923             }
7924
7925             Roo.log("keypress blocked");
7926
7927             e.preventDefault();
7928             return false;
7929         });
7930         
7931     },
7932     // private
7933     onSubmit : function(e){
7934         e.stopEvent();
7935     },
7936
7937      /**
7938      * Returns true if client-side validation on the form is successful.
7939      * @return Boolean
7940      */
7941     isValid : function(){
7942         var items = this.getItems();
7943         var valid = true;
7944         var target = false;
7945         
7946         items.each(function(f){
7947             
7948             if(f.validate()){
7949                 return;
7950             }
7951             
7952             Roo.log('invalid field: ' + f.name);
7953             
7954             valid = false;
7955
7956             if(!target && f.el.isVisible(true)){
7957                 target = f;
7958             }
7959            
7960         });
7961         
7962         if(this.errorMask && !valid){
7963             Roo.bootstrap.Form.popover.mask(this, target);
7964         }
7965         
7966         return valid;
7967     },
7968     
7969     /**
7970      * Returns true if any fields in this form have changed since their original load.
7971      * @return Boolean
7972      */
7973     isDirty : function(){
7974         var dirty = false;
7975         var items = this.getItems();
7976         items.each(function(f){
7977            if(f.isDirty()){
7978                dirty = true;
7979                return false;
7980            }
7981            return true;
7982         });
7983         return dirty;
7984     },
7985      /**
7986      * Performs a predefined action (submit or load) or custom actions you define on this form.
7987      * @param {String} actionName The name of the action type
7988      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7989      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7990      * accept other config options):
7991      * <pre>
7992 Property          Type             Description
7993 ----------------  ---------------  ----------------------------------------------------------------------------------
7994 url               String           The url for the action (defaults to the form's url)
7995 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7996 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7997 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7998                                    validate the form on the client (defaults to false)
7999      * </pre>
8000      * @return {BasicForm} this
8001      */
8002     doAction : function(action, options){
8003         if(typeof action == 'string'){
8004             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8005         }
8006         if(this.fireEvent('beforeaction', this, action) !== false){
8007             this.beforeAction(action);
8008             action.run.defer(100, action);
8009         }
8010         return this;
8011     },
8012
8013     // private
8014     beforeAction : function(action){
8015         var o = action.options;
8016         
8017         if(this.loadMask){
8018             
8019             if(this.maskBody){
8020                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8021             } else {
8022                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8023             }
8024         }
8025         // not really supported yet.. ??
8026
8027         //if(this.waitMsgTarget === true){
8028         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8029         //}else if(this.waitMsgTarget){
8030         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8031         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8032         //}else {
8033         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8034        // }
8035
8036     },
8037
8038     // private
8039     afterAction : function(action, success){
8040         this.activeAction = null;
8041         var o = action.options;
8042
8043         if(this.loadMask){
8044             
8045             if(this.maskBody){
8046                 Roo.get(document.body).unmask();
8047             } else {
8048                 this.el.unmask();
8049             }
8050         }
8051         
8052         //if(this.waitMsgTarget === true){
8053 //            this.el.unmask();
8054         //}else if(this.waitMsgTarget){
8055         //    this.waitMsgTarget.unmask();
8056         //}else{
8057         //    Roo.MessageBox.updateProgress(1);
8058         //    Roo.MessageBox.hide();
8059        // }
8060         //
8061         if(success){
8062             if(o.reset){
8063                 this.reset();
8064             }
8065             Roo.callback(o.success, o.scope, [this, action]);
8066             this.fireEvent('actioncomplete', this, action);
8067
8068         }else{
8069
8070             // failure condition..
8071             // we have a scenario where updates need confirming.
8072             // eg. if a locking scenario exists..
8073             // we look for { errors : { needs_confirm : true }} in the response.
8074             if (
8075                 (typeof(action.result) != 'undefined')  &&
8076                 (typeof(action.result.errors) != 'undefined')  &&
8077                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8078            ){
8079                 var _t = this;
8080                 Roo.log("not supported yet");
8081                  /*
8082
8083                 Roo.MessageBox.confirm(
8084                     "Change requires confirmation",
8085                     action.result.errorMsg,
8086                     function(r) {
8087                         if (r != 'yes') {
8088                             return;
8089                         }
8090                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8091                     }
8092
8093                 );
8094                 */
8095
8096
8097                 return;
8098             }
8099
8100             Roo.callback(o.failure, o.scope, [this, action]);
8101             // show an error message if no failed handler is set..
8102             if (!this.hasListener('actionfailed')) {
8103                 Roo.log("need to add dialog support");
8104                 /*
8105                 Roo.MessageBox.alert("Error",
8106                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8107                         action.result.errorMsg :
8108                         "Saving Failed, please check your entries or try again"
8109                 );
8110                 */
8111             }
8112
8113             this.fireEvent('actionfailed', this, action);
8114         }
8115
8116     },
8117     /**
8118      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8119      * @param {String} id The value to search for
8120      * @return Field
8121      */
8122     findField : function(id){
8123         var items = this.getItems();
8124         var field = items.get(id);
8125         if(!field){
8126              items.each(function(f){
8127                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8128                     field = f;
8129                     return false;
8130                 }
8131                 return true;
8132             });
8133         }
8134         return field || null;
8135     },
8136      /**
8137      * Mark fields in this form invalid in bulk.
8138      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8139      * @return {BasicForm} this
8140      */
8141     markInvalid : function(errors){
8142         if(errors instanceof Array){
8143             for(var i = 0, len = errors.length; i < len; i++){
8144                 var fieldError = errors[i];
8145                 var f = this.findField(fieldError.id);
8146                 if(f){
8147                     f.markInvalid(fieldError.msg);
8148                 }
8149             }
8150         }else{
8151             var field, id;
8152             for(id in errors){
8153                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8154                     field.markInvalid(errors[id]);
8155                 }
8156             }
8157         }
8158         //Roo.each(this.childForms || [], function (f) {
8159         //    f.markInvalid(errors);
8160         //});
8161
8162         return this;
8163     },
8164
8165     /**
8166      * Set values for fields in this form in bulk.
8167      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8168      * @return {BasicForm} this
8169      */
8170     setValues : function(values){
8171         if(values instanceof Array){ // array of objects
8172             for(var i = 0, len = values.length; i < len; i++){
8173                 var v = values[i];
8174                 var f = this.findField(v.id);
8175                 if(f){
8176                     f.setValue(v.value);
8177                     if(this.trackResetOnLoad){
8178                         f.originalValue = f.getValue();
8179                     }
8180                 }
8181             }
8182         }else{ // object hash
8183             var field, id;
8184             for(id in values){
8185                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8186
8187                     if (field.setFromData &&
8188                         field.valueField &&
8189                         field.displayField &&
8190                         // combos' with local stores can
8191                         // be queried via setValue()
8192                         // to set their value..
8193                         (field.store && !field.store.isLocal)
8194                         ) {
8195                         // it's a combo
8196                         var sd = { };
8197                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8198                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8199                         field.setFromData(sd);
8200
8201                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8202                         
8203                         field.setFromData(values);
8204                         
8205                     } else {
8206                         field.setValue(values[id]);
8207                     }
8208
8209
8210                     if(this.trackResetOnLoad){
8211                         field.originalValue = field.getValue();
8212                     }
8213                 }
8214             }
8215         }
8216
8217         //Roo.each(this.childForms || [], function (f) {
8218         //    f.setValues(values);
8219         //});
8220
8221         return this;
8222     },
8223
8224     /**
8225      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8226      * they are returned as an array.
8227      * @param {Boolean} asString
8228      * @return {Object}
8229      */
8230     getValues : function(asString){
8231         //if (this.childForms) {
8232             // copy values from the child forms
8233         //    Roo.each(this.childForms, function (f) {
8234         //        this.setValues(f.getValues());
8235         //    }, this);
8236         //}
8237
8238
8239
8240         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8241         if(asString === true){
8242             return fs;
8243         }
8244         return Roo.urlDecode(fs);
8245     },
8246
8247     /**
8248      * Returns the fields in this form as an object with key/value pairs.
8249      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8250      * @return {Object}
8251      */
8252     getFieldValues : function(with_hidden)
8253     {
8254         var items = this.getItems();
8255         var ret = {};
8256         items.each(function(f){
8257             
8258             if (!f.getName()) {
8259                 return;
8260             }
8261             
8262             var v = f.getValue();
8263             
8264             if (f.inputType =='radio') {
8265                 if (typeof(ret[f.getName()]) == 'undefined') {
8266                     ret[f.getName()] = ''; // empty..
8267                 }
8268
8269                 if (!f.el.dom.checked) {
8270                     return;
8271
8272                 }
8273                 v = f.el.dom.value;
8274
8275             }
8276             
8277             if(f.xtype == 'MoneyField'){
8278                 ret[f.currencyName] = f.getCurrency();
8279             }
8280
8281             // not sure if this supported any more..
8282             if ((typeof(v) == 'object') && f.getRawValue) {
8283                 v = f.getRawValue() ; // dates..
8284             }
8285             // combo boxes where name != hiddenName...
8286             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8287                 ret[f.name] = f.getRawValue();
8288             }
8289             ret[f.getName()] = v;
8290         });
8291
8292         return ret;
8293     },
8294
8295     /**
8296      * Clears all invalid messages in this form.
8297      * @return {BasicForm} this
8298      */
8299     clearInvalid : function(){
8300         var items = this.getItems();
8301
8302         items.each(function(f){
8303            f.clearInvalid();
8304         });
8305
8306         return this;
8307     },
8308
8309     /**
8310      * Resets this form.
8311      * @return {BasicForm} this
8312      */
8313     reset : function(){
8314         var items = this.getItems();
8315         items.each(function(f){
8316             f.reset();
8317         });
8318
8319         Roo.each(this.childForms || [], function (f) {
8320             f.reset();
8321         });
8322
8323
8324         return this;
8325     },
8326     
8327     getItems : function()
8328     {
8329         var r=new Roo.util.MixedCollection(false, function(o){
8330             return o.id || (o.id = Roo.id());
8331         });
8332         var iter = function(el) {
8333             if (el.inputEl) {
8334                 r.add(el);
8335             }
8336             if (!el.items) {
8337                 return;
8338             }
8339             Roo.each(el.items,function(e) {
8340                 iter(e);
8341             });
8342         };
8343
8344         iter(this);
8345         return r;
8346     },
8347     
8348     hideFields : function(items)
8349     {
8350         Roo.each(items, function(i){
8351             
8352             var f = this.findField(i);
8353             
8354             if(!f){
8355                 return;
8356             }
8357             
8358             f.hide();
8359             
8360         }, this);
8361     },
8362     
8363     showFields : function(items)
8364     {
8365         Roo.each(items, function(i){
8366             
8367             var f = this.findField(i);
8368             
8369             if(!f){
8370                 return;
8371             }
8372             
8373             f.show();
8374             
8375         }, this);
8376     }
8377
8378 });
8379
8380 Roo.apply(Roo.bootstrap.Form, {
8381     
8382     popover : {
8383         
8384         padding : 5,
8385         
8386         isApplied : false,
8387         
8388         isMasked : false,
8389         
8390         form : false,
8391         
8392         target : false,
8393         
8394         toolTip : false,
8395         
8396         intervalID : false,
8397         
8398         maskEl : false,
8399         
8400         apply : function()
8401         {
8402             if(this.isApplied){
8403                 return;
8404             }
8405             
8406             this.maskEl = {
8407                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8408                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8409                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8410                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8411             };
8412             
8413             this.maskEl.top.enableDisplayMode("block");
8414             this.maskEl.left.enableDisplayMode("block");
8415             this.maskEl.bottom.enableDisplayMode("block");
8416             this.maskEl.right.enableDisplayMode("block");
8417             
8418             this.toolTip = new Roo.bootstrap.Tooltip({
8419                 cls : 'roo-form-error-popover',
8420                 alignment : {
8421                     'left' : ['r-l', [-2,0], 'right'],
8422                     'right' : ['l-r', [2,0], 'left'],
8423                     'bottom' : ['tl-bl', [0,2], 'top'],
8424                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8425                 }
8426             });
8427             
8428             this.toolTip.render(Roo.get(document.body));
8429
8430             this.toolTip.el.enableDisplayMode("block");
8431             
8432             Roo.get(document.body).on('click', function(){
8433                 this.unmask();
8434             }, this);
8435             
8436             Roo.get(document.body).on('touchstart', function(){
8437                 this.unmask();
8438             }, this);
8439             
8440             this.isApplied = true
8441         },
8442         
8443         mask : function(form, target)
8444         {
8445             this.form = form;
8446             
8447             this.target = target;
8448             
8449             if(!this.form.errorMask || !target.el){
8450                 return;
8451             }
8452             
8453             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8454             
8455             Roo.log(scrollable);
8456             
8457             var ot = this.target.el.calcOffsetsTo(scrollable);
8458             
8459             var scrollTo = ot[1] - this.form.maskOffset;
8460             
8461             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8462             
8463             scrollable.scrollTo('top', scrollTo);
8464             
8465             var box = this.target.el.getBox();
8466             Roo.log(box);
8467             var zIndex = Roo.bootstrap.Modal.zIndex++;
8468
8469             
8470             this.maskEl.top.setStyle('position', 'absolute');
8471             this.maskEl.top.setStyle('z-index', zIndex);
8472             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8473             this.maskEl.top.setLeft(0);
8474             this.maskEl.top.setTop(0);
8475             this.maskEl.top.show();
8476             
8477             this.maskEl.left.setStyle('position', 'absolute');
8478             this.maskEl.left.setStyle('z-index', zIndex);
8479             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8480             this.maskEl.left.setLeft(0);
8481             this.maskEl.left.setTop(box.y - this.padding);
8482             this.maskEl.left.show();
8483
8484             this.maskEl.bottom.setStyle('position', 'absolute');
8485             this.maskEl.bottom.setStyle('z-index', zIndex);
8486             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8487             this.maskEl.bottom.setLeft(0);
8488             this.maskEl.bottom.setTop(box.bottom + this.padding);
8489             this.maskEl.bottom.show();
8490
8491             this.maskEl.right.setStyle('position', 'absolute');
8492             this.maskEl.right.setStyle('z-index', zIndex);
8493             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8494             this.maskEl.right.setLeft(box.right + this.padding);
8495             this.maskEl.right.setTop(box.y - this.padding);
8496             this.maskEl.right.show();
8497
8498             this.toolTip.bindEl = this.target.el;
8499
8500             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8501
8502             var tip = this.target.blankText;
8503
8504             if(this.target.getValue() !== '' ) {
8505                 
8506                 if (this.target.invalidText.length) {
8507                     tip = this.target.invalidText;
8508                 } else if (this.target.regexText.length){
8509                     tip = this.target.regexText;
8510                 }
8511             }
8512
8513             this.toolTip.show(tip);
8514
8515             this.intervalID = window.setInterval(function() {
8516                 Roo.bootstrap.Form.popover.unmask();
8517             }, 10000);
8518
8519             window.onwheel = function(){ return false;};
8520             
8521             (function(){ this.isMasked = true; }).defer(500, this);
8522             
8523         },
8524         
8525         unmask : function()
8526         {
8527             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8528                 return;
8529             }
8530             
8531             this.maskEl.top.setStyle('position', 'absolute');
8532             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8533             this.maskEl.top.hide();
8534
8535             this.maskEl.left.setStyle('position', 'absolute');
8536             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8537             this.maskEl.left.hide();
8538
8539             this.maskEl.bottom.setStyle('position', 'absolute');
8540             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8541             this.maskEl.bottom.hide();
8542
8543             this.maskEl.right.setStyle('position', 'absolute');
8544             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8545             this.maskEl.right.hide();
8546             
8547             this.toolTip.hide();
8548             
8549             this.toolTip.el.hide();
8550             
8551             window.onwheel = function(){ return true;};
8552             
8553             if(this.intervalID){
8554                 window.clearInterval(this.intervalID);
8555                 this.intervalID = false;
8556             }
8557             
8558             this.isMasked = false;
8559             
8560         }
8561         
8562     }
8563     
8564 });
8565
8566 /*
8567  * Based on:
8568  * Ext JS Library 1.1.1
8569  * Copyright(c) 2006-2007, Ext JS, LLC.
8570  *
8571  * Originally Released Under LGPL - original licence link has changed is not relivant.
8572  *
8573  * Fork - LGPL
8574  * <script type="text/javascript">
8575  */
8576 /**
8577  * @class Roo.form.VTypes
8578  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8579  * @singleton
8580  */
8581 Roo.form.VTypes = function(){
8582     // closure these in so they are only created once.
8583     var alpha = /^[a-zA-Z_]+$/;
8584     var alphanum = /^[a-zA-Z0-9_]+$/;
8585     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8586     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8587
8588     // All these messages and functions are configurable
8589     return {
8590         /**
8591          * The function used to validate email addresses
8592          * @param {String} value The email address
8593          */
8594         'email' : function(v){
8595             return email.test(v);
8596         },
8597         /**
8598          * The error text to display when the email validation function returns false
8599          * @type String
8600          */
8601         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8602         /**
8603          * The keystroke filter mask to be applied on email input
8604          * @type RegExp
8605          */
8606         'emailMask' : /[a-z0-9_\.\-@]/i,
8607
8608         /**
8609          * The function used to validate URLs
8610          * @param {String} value The URL
8611          */
8612         'url' : function(v){
8613             return url.test(v);
8614         },
8615         /**
8616          * The error text to display when the url validation function returns false
8617          * @type String
8618          */
8619         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8620         
8621         /**
8622          * The function used to validate alpha values
8623          * @param {String} value The value
8624          */
8625         'alpha' : function(v){
8626             return alpha.test(v);
8627         },
8628         /**
8629          * The error text to display when the alpha validation function returns false
8630          * @type String
8631          */
8632         'alphaText' : 'This field should only contain letters and _',
8633         /**
8634          * The keystroke filter mask to be applied on alpha input
8635          * @type RegExp
8636          */
8637         'alphaMask' : /[a-z_]/i,
8638
8639         /**
8640          * The function used to validate alphanumeric values
8641          * @param {String} value The value
8642          */
8643         'alphanum' : function(v){
8644             return alphanum.test(v);
8645         },
8646         /**
8647          * The error text to display when the alphanumeric validation function returns false
8648          * @type String
8649          */
8650         'alphanumText' : 'This field should only contain letters, numbers and _',
8651         /**
8652          * The keystroke filter mask to be applied on alphanumeric input
8653          * @type RegExp
8654          */
8655         'alphanumMask' : /[a-z0-9_]/i
8656     };
8657 }();/*
8658  * - LGPL
8659  *
8660  * Input
8661  * 
8662  */
8663
8664 /**
8665  * @class Roo.bootstrap.Input
8666  * @extends Roo.bootstrap.Component
8667  * Bootstrap Input class
8668  * @cfg {Boolean} disabled is it disabled
8669  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8670  * @cfg {String} name name of the input
8671  * @cfg {string} fieldLabel - the label associated
8672  * @cfg {string} placeholder - placeholder to put in text.
8673  * @cfg {string}  before - input group add on before
8674  * @cfg {string} after - input group add on after
8675  * @cfg {string} size - (lg|sm) or leave empty..
8676  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8677  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8678  * @cfg {Number} md colspan out of 12 for computer-sized screens
8679  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8680  * @cfg {string} value default value of the input
8681  * @cfg {Number} labelWidth set the width of label 
8682  * @cfg {Number} labellg set the width of label (1-12)
8683  * @cfg {Number} labelmd set the width of label (1-12)
8684  * @cfg {Number} labelsm set the width of label (1-12)
8685  * @cfg {Number} labelxs set the width of label (1-12)
8686  * @cfg {String} labelAlign (top|left)
8687  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8688  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8689  * @cfg {String} indicatorpos (left|right) default left
8690  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8691  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8692
8693  * @cfg {String} align (left|center|right) Default left
8694  * @cfg {Boolean} forceFeedback (true|false) Default false
8695  * 
8696  * @constructor
8697  * Create a new Input
8698  * @param {Object} config The config object
8699  */
8700
8701 Roo.bootstrap.Input = function(config){
8702     
8703     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8704     
8705     this.addEvents({
8706         /**
8707          * @event focus
8708          * Fires when this field receives input focus.
8709          * @param {Roo.form.Field} this
8710          */
8711         focus : true,
8712         /**
8713          * @event blur
8714          * Fires when this field loses input focus.
8715          * @param {Roo.form.Field} this
8716          */
8717         blur : true,
8718         /**
8719          * @event specialkey
8720          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8721          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8722          * @param {Roo.form.Field} this
8723          * @param {Roo.EventObject} e The event object
8724          */
8725         specialkey : true,
8726         /**
8727          * @event change
8728          * Fires just before the field blurs if the field value has changed.
8729          * @param {Roo.form.Field} this
8730          * @param {Mixed} newValue The new value
8731          * @param {Mixed} oldValue The original value
8732          */
8733         change : true,
8734         /**
8735          * @event invalid
8736          * Fires after the field has been marked as invalid.
8737          * @param {Roo.form.Field} this
8738          * @param {String} msg The validation message
8739          */
8740         invalid : true,
8741         /**
8742          * @event valid
8743          * Fires after the field has been validated with no errors.
8744          * @param {Roo.form.Field} this
8745          */
8746         valid : true,
8747          /**
8748          * @event keyup
8749          * Fires after the key up
8750          * @param {Roo.form.Field} this
8751          * @param {Roo.EventObject}  e The event Object
8752          */
8753         keyup : true
8754     });
8755 };
8756
8757 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8758      /**
8759      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8760       automatic validation (defaults to "keyup").
8761      */
8762     validationEvent : "keyup",
8763      /**
8764      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8765      */
8766     validateOnBlur : true,
8767     /**
8768      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8769      */
8770     validationDelay : 250,
8771      /**
8772      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8773      */
8774     focusClass : "x-form-focus",  // not needed???
8775     
8776        
8777     /**
8778      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8779      */
8780     invalidClass : "has-warning",
8781     
8782     /**
8783      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8784      */
8785     validClass : "has-success",
8786     
8787     /**
8788      * @cfg {Boolean} hasFeedback (true|false) default true
8789      */
8790     hasFeedback : true,
8791     
8792     /**
8793      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8794      */
8795     invalidFeedbackClass : "glyphicon-warning-sign",
8796     
8797     /**
8798      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8799      */
8800     validFeedbackClass : "glyphicon-ok",
8801     
8802     /**
8803      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8804      */
8805     selectOnFocus : false,
8806     
8807      /**
8808      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8809      */
8810     maskRe : null,
8811        /**
8812      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8813      */
8814     vtype : null,
8815     
8816       /**
8817      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8818      */
8819     disableKeyFilter : false,
8820     
8821        /**
8822      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8823      */
8824     disabled : false,
8825      /**
8826      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8827      */
8828     allowBlank : true,
8829     /**
8830      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8831      */
8832     blankText : "Please complete this mandatory field",
8833     
8834      /**
8835      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8836      */
8837     minLength : 0,
8838     /**
8839      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8840      */
8841     maxLength : Number.MAX_VALUE,
8842     /**
8843      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8844      */
8845     minLengthText : "The minimum length for this field is {0}",
8846     /**
8847      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8848      */
8849     maxLengthText : "The maximum length for this field is {0}",
8850   
8851     
8852     /**
8853      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8854      * If available, this function will be called only after the basic validators all return true, and will be passed the
8855      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8856      */
8857     validator : null,
8858     /**
8859      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8860      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8861      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8862      */
8863     regex : null,
8864     /**
8865      * @cfg {String} regexText -- Depricated - use Invalid Text
8866      */
8867     regexText : "",
8868     
8869     /**
8870      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8871      */
8872     invalidText : "",
8873     
8874     
8875     
8876     autocomplete: false,
8877     
8878     
8879     fieldLabel : '',
8880     inputType : 'text',
8881     
8882     name : false,
8883     placeholder: false,
8884     before : false,
8885     after : false,
8886     size : false,
8887     hasFocus : false,
8888     preventMark: false,
8889     isFormField : true,
8890     value : '',
8891     labelWidth : 2,
8892     labelAlign : false,
8893     readOnly : false,
8894     align : false,
8895     formatedValue : false,
8896     forceFeedback : false,
8897     
8898     indicatorpos : 'left',
8899     
8900     labellg : 0,
8901     labelmd : 0,
8902     labelsm : 0,
8903     labelxs : 0,
8904     
8905     capture : '',
8906     accept : '',
8907     
8908     parentLabelAlign : function()
8909     {
8910         var parent = this;
8911         while (parent.parent()) {
8912             parent = parent.parent();
8913             if (typeof(parent.labelAlign) !='undefined') {
8914                 return parent.labelAlign;
8915             }
8916         }
8917         return 'left';
8918         
8919     },
8920     
8921     getAutoCreate : function()
8922     {
8923         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8924         
8925         var id = Roo.id();
8926         
8927         var cfg = {};
8928         
8929         if(this.inputType != 'hidden'){
8930             cfg.cls = 'form-group' //input-group
8931         }
8932         
8933         var input =  {
8934             tag: 'input',
8935             id : id,
8936             type : this.inputType,
8937             value : this.value,
8938             cls : 'form-control',
8939             placeholder : this.placeholder || '',
8940             autocomplete : this.autocomplete || 'new-password'
8941         };
8942         
8943         if(this.capture.length){
8944             input.capture = this.capture;
8945         }
8946         
8947         if(this.accept.length){
8948             input.accept = this.accept + "/*";
8949         }
8950         
8951         if(this.align){
8952             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8953         }
8954         
8955         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8956             input.maxLength = this.maxLength;
8957         }
8958         
8959         if (this.disabled) {
8960             input.disabled=true;
8961         }
8962         
8963         if (this.readOnly) {
8964             input.readonly=true;
8965         }
8966         
8967         if (this.name) {
8968             input.name = this.name;
8969         }
8970         
8971         if (this.size) {
8972             input.cls += ' input-' + this.size;
8973         }
8974         
8975         var settings=this;
8976         ['xs','sm','md','lg'].map(function(size){
8977             if (settings[size]) {
8978                 cfg.cls += ' col-' + size + '-' + settings[size];
8979             }
8980         });
8981         
8982         var inputblock = input;
8983         
8984         var feedback = {
8985             tag: 'span',
8986             cls: 'glyphicon form-control-feedback'
8987         };
8988             
8989         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8990             
8991             inputblock = {
8992                 cls : 'has-feedback',
8993                 cn :  [
8994                     input,
8995                     feedback
8996                 ] 
8997             };  
8998         }
8999         
9000         if (this.before || this.after) {
9001             
9002             inputblock = {
9003                 cls : 'input-group',
9004                 cn :  [] 
9005             };
9006             
9007             if (this.before && typeof(this.before) == 'string') {
9008                 
9009                 inputblock.cn.push({
9010                     tag :'span',
9011                     cls : 'roo-input-before input-group-addon',
9012                     html : this.before
9013                 });
9014             }
9015             if (this.before && typeof(this.before) == 'object') {
9016                 this.before = Roo.factory(this.before);
9017                 
9018                 inputblock.cn.push({
9019                     tag :'span',
9020                     cls : 'roo-input-before input-group-' +
9021                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9022                 });
9023             }
9024             
9025             inputblock.cn.push(input);
9026             
9027             if (this.after && typeof(this.after) == 'string') {
9028                 inputblock.cn.push({
9029                     tag :'span',
9030                     cls : 'roo-input-after input-group-addon',
9031                     html : this.after
9032                 });
9033             }
9034             if (this.after && typeof(this.after) == 'object') {
9035                 this.after = Roo.factory(this.after);
9036                 
9037                 inputblock.cn.push({
9038                     tag :'span',
9039                     cls : 'roo-input-after input-group-' +
9040                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9041                 });
9042             }
9043             
9044             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9045                 inputblock.cls += ' has-feedback';
9046                 inputblock.cn.push(feedback);
9047             }
9048         };
9049         
9050         if (align ==='left' && this.fieldLabel.length) {
9051             
9052             cfg.cls += ' roo-form-group-label-left';
9053             
9054             cfg.cn = [
9055                 {
9056                     tag : 'i',
9057                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9058                     tooltip : 'This field is required'
9059                 },
9060                 {
9061                     tag: 'label',
9062                     'for' :  id,
9063                     cls : 'control-label',
9064                     html : this.fieldLabel
9065
9066                 },
9067                 {
9068                     cls : "", 
9069                     cn: [
9070                         inputblock
9071                     ]
9072                 }
9073             ];
9074             
9075             var labelCfg = cfg.cn[1];
9076             var contentCfg = cfg.cn[2];
9077             
9078             if(this.indicatorpos == 'right'){
9079                 cfg.cn = [
9080                     {
9081                         tag: 'label',
9082                         'for' :  id,
9083                         cls : 'control-label',
9084                         cn : [
9085                             {
9086                                 tag : 'span',
9087                                 html : this.fieldLabel
9088                             },
9089                             {
9090                                 tag : 'i',
9091                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9092                                 tooltip : 'This field is required'
9093                             }
9094                         ]
9095                     },
9096                     {
9097                         cls : "",
9098                         cn: [
9099                             inputblock
9100                         ]
9101                     }
9102
9103                 ];
9104                 
9105                 labelCfg = cfg.cn[0];
9106                 contentCfg = cfg.cn[1];
9107             
9108             }
9109             
9110             if(this.labelWidth > 12){
9111                 labelCfg.style = "width: " + this.labelWidth + 'px';
9112             }
9113             
9114             if(this.labelWidth < 13 && this.labelmd == 0){
9115                 this.labelmd = this.labelWidth;
9116             }
9117             
9118             if(this.labellg > 0){
9119                 labelCfg.cls += ' col-lg-' + this.labellg;
9120                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9121             }
9122             
9123             if(this.labelmd > 0){
9124                 labelCfg.cls += ' col-md-' + this.labelmd;
9125                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9126             }
9127             
9128             if(this.labelsm > 0){
9129                 labelCfg.cls += ' col-sm-' + this.labelsm;
9130                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9131             }
9132             
9133             if(this.labelxs > 0){
9134                 labelCfg.cls += ' col-xs-' + this.labelxs;
9135                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9136             }
9137             
9138             
9139         } else if ( this.fieldLabel.length) {
9140                 
9141             cfg.cn = [
9142                 {
9143                     tag : 'i',
9144                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9145                     tooltip : 'This field is required'
9146                 },
9147                 {
9148                     tag: 'label',
9149                    //cls : 'input-group-addon',
9150                     html : this.fieldLabel
9151
9152                 },
9153
9154                inputblock
9155
9156            ];
9157            
9158            if(this.indicatorpos == 'right'){
9159                 
9160                 cfg.cn = [
9161                     {
9162                         tag: 'label',
9163                        //cls : 'input-group-addon',
9164                         html : this.fieldLabel
9165
9166                     },
9167                     {
9168                         tag : 'i',
9169                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9170                         tooltip : 'This field is required'
9171                     },
9172
9173                    inputblock
9174
9175                ];
9176
9177             }
9178
9179         } else {
9180             
9181             cfg.cn = [
9182
9183                     inputblock
9184
9185             ];
9186                 
9187                 
9188         };
9189         
9190         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9191            cfg.cls += ' navbar-form';
9192         }
9193         
9194         if (this.parentType === 'NavGroup') {
9195            cfg.cls += ' navbar-form';
9196            cfg.tag = 'li';
9197         }
9198         
9199         return cfg;
9200         
9201     },
9202     /**
9203      * return the real input element.
9204      */
9205     inputEl: function ()
9206     {
9207         return this.el.select('input.form-control',true).first();
9208     },
9209     
9210     tooltipEl : function()
9211     {
9212         return this.inputEl();
9213     },
9214     
9215     indicatorEl : function()
9216     {
9217         var indicator = this.el.select('i.roo-required-indicator',true).first();
9218         
9219         if(!indicator){
9220             return false;
9221         }
9222         
9223         return indicator;
9224         
9225     },
9226     
9227     setDisabled : function(v)
9228     {
9229         var i  = this.inputEl().dom;
9230         if (!v) {
9231             i.removeAttribute('disabled');
9232             return;
9233             
9234         }
9235         i.setAttribute('disabled','true');
9236     },
9237     initEvents : function()
9238     {
9239           
9240         this.inputEl().on("keydown" , this.fireKey,  this);
9241         this.inputEl().on("focus", this.onFocus,  this);
9242         this.inputEl().on("blur", this.onBlur,  this);
9243         
9244         this.inputEl().relayEvent('keyup', this);
9245         
9246         this.indicator = this.indicatorEl();
9247         
9248         if(this.indicator){
9249             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9250         }
9251  
9252         // reference to original value for reset
9253         this.originalValue = this.getValue();
9254         //Roo.form.TextField.superclass.initEvents.call(this);
9255         if(this.validationEvent == 'keyup'){
9256             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9257             this.inputEl().on('keyup', this.filterValidation, this);
9258         }
9259         else if(this.validationEvent !== false){
9260             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9261         }
9262         
9263         if(this.selectOnFocus){
9264             this.on("focus", this.preFocus, this);
9265             
9266         }
9267         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9268             this.inputEl().on("keypress", this.filterKeys, this);
9269         } else {
9270             this.inputEl().relayEvent('keypress', this);
9271         }
9272        /* if(this.grow){
9273             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9274             this.el.on("click", this.autoSize,  this);
9275         }
9276         */
9277         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9278             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9279         }
9280         
9281         if (typeof(this.before) == 'object') {
9282             this.before.render(this.el.select('.roo-input-before',true).first());
9283         }
9284         if (typeof(this.after) == 'object') {
9285             this.after.render(this.el.select('.roo-input-after',true).first());
9286         }
9287         
9288         this.inputEl().on('change', this.onChange, this);
9289         
9290     },
9291     filterValidation : function(e){
9292         if(!e.isNavKeyPress()){
9293             this.validationTask.delay(this.validationDelay);
9294         }
9295     },
9296      /**
9297      * Validates the field value
9298      * @return {Boolean} True if the value is valid, else false
9299      */
9300     validate : function(){
9301         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9302         if(this.disabled || this.validateValue(this.getRawValue())){
9303             this.markValid();
9304             return true;
9305         }
9306         
9307         this.markInvalid();
9308         return false;
9309     },
9310     
9311     
9312     /**
9313      * Validates a value according to the field's validation rules and marks the field as invalid
9314      * if the validation fails
9315      * @param {Mixed} value The value to validate
9316      * @return {Boolean} True if the value is valid, else false
9317      */
9318     validateValue : function(value)
9319     {
9320         if(this.getVisibilityEl().hasClass('hidden')){
9321             return true;
9322         }
9323         
9324         if(value.length < 1)  { // if it's blank
9325             if(this.allowBlank){
9326                 return true;
9327             }
9328             return false;
9329         }
9330         
9331         if(value.length < this.minLength){
9332             return false;
9333         }
9334         if(value.length > this.maxLength){
9335             return false;
9336         }
9337         if(this.vtype){
9338             var vt = Roo.form.VTypes;
9339             if(!vt[this.vtype](value, this)){
9340                 return false;
9341             }
9342         }
9343         if(typeof this.validator == "function"){
9344             var msg = this.validator(value);
9345             if(msg !== true){
9346                 return false;
9347             }
9348             if (typeof(msg) == 'string') {
9349                 this.invalidText = msg;
9350             }
9351         }
9352         
9353         if(this.regex && !this.regex.test(value)){
9354             return false;
9355         }
9356         
9357         return true;
9358     },
9359     
9360      // private
9361     fireKey : function(e){
9362         //Roo.log('field ' + e.getKey());
9363         if(e.isNavKeyPress()){
9364             this.fireEvent("specialkey", this, e);
9365         }
9366     },
9367     focus : function (selectText){
9368         if(this.rendered){
9369             this.inputEl().focus();
9370             if(selectText === true){
9371                 this.inputEl().dom.select();
9372             }
9373         }
9374         return this;
9375     } ,
9376     
9377     onFocus : function(){
9378         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9379            // this.el.addClass(this.focusClass);
9380         }
9381         if(!this.hasFocus){
9382             this.hasFocus = true;
9383             this.startValue = this.getValue();
9384             this.fireEvent("focus", this);
9385         }
9386     },
9387     
9388     beforeBlur : Roo.emptyFn,
9389
9390     
9391     // private
9392     onBlur : function(){
9393         this.beforeBlur();
9394         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9395             //this.el.removeClass(this.focusClass);
9396         }
9397         this.hasFocus = false;
9398         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9399             this.validate();
9400         }
9401         var v = this.getValue();
9402         if(String(v) !== String(this.startValue)){
9403             this.fireEvent('change', this, v, this.startValue);
9404         }
9405         this.fireEvent("blur", this);
9406     },
9407     
9408     onChange : function(e)
9409     {
9410         var v = this.getValue();
9411         if(String(v) !== String(this.startValue)){
9412             this.fireEvent('change', this, v, this.startValue);
9413         }
9414         
9415     },
9416     
9417     /**
9418      * Resets the current field value to the originally loaded value and clears any validation messages
9419      */
9420     reset : function(){
9421         this.setValue(this.originalValue);
9422         this.validate();
9423     },
9424      /**
9425      * Returns the name of the field
9426      * @return {Mixed} name The name field
9427      */
9428     getName: function(){
9429         return this.name;
9430     },
9431      /**
9432      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9433      * @return {Mixed} value The field value
9434      */
9435     getValue : function(){
9436         
9437         var v = this.inputEl().getValue();
9438         
9439         return v;
9440     },
9441     /**
9442      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9443      * @return {Mixed} value The field value
9444      */
9445     getRawValue : function(){
9446         var v = this.inputEl().getValue();
9447         
9448         return v;
9449     },
9450     
9451     /**
9452      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9453      * @param {Mixed} value The value to set
9454      */
9455     setRawValue : function(v){
9456         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9457     },
9458     
9459     selectText : function(start, end){
9460         var v = this.getRawValue();
9461         if(v.length > 0){
9462             start = start === undefined ? 0 : start;
9463             end = end === undefined ? v.length : end;
9464             var d = this.inputEl().dom;
9465             if(d.setSelectionRange){
9466                 d.setSelectionRange(start, end);
9467             }else if(d.createTextRange){
9468                 var range = d.createTextRange();
9469                 range.moveStart("character", start);
9470                 range.moveEnd("character", v.length-end);
9471                 range.select();
9472             }
9473         }
9474     },
9475     
9476     /**
9477      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9478      * @param {Mixed} value The value to set
9479      */
9480     setValue : function(v){
9481         this.value = v;
9482         if(this.rendered){
9483             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9484             this.validate();
9485         }
9486     },
9487     
9488     /*
9489     processValue : function(value){
9490         if(this.stripCharsRe){
9491             var newValue = value.replace(this.stripCharsRe, '');
9492             if(newValue !== value){
9493                 this.setRawValue(newValue);
9494                 return newValue;
9495             }
9496         }
9497         return value;
9498     },
9499   */
9500     preFocus : function(){
9501         
9502         if(this.selectOnFocus){
9503             this.inputEl().dom.select();
9504         }
9505     },
9506     filterKeys : function(e){
9507         var k = e.getKey();
9508         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9509             return;
9510         }
9511         var c = e.getCharCode(), cc = String.fromCharCode(c);
9512         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9513             return;
9514         }
9515         if(!this.maskRe.test(cc)){
9516             e.stopEvent();
9517         }
9518     },
9519      /**
9520      * Clear any invalid styles/messages for this field
9521      */
9522     clearInvalid : function(){
9523         
9524         if(!this.el || this.preventMark){ // not rendered
9525             return;
9526         }
9527         
9528      
9529         this.el.removeClass(this.invalidClass);
9530         
9531         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9532             
9533             var feedback = this.el.select('.form-control-feedback', true).first();
9534             
9535             if(feedback){
9536                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9537             }
9538             
9539         }
9540         
9541         if(this.indicator){
9542             this.indicator.removeClass('visible');
9543             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9544         }
9545         
9546         this.fireEvent('valid', this);
9547     },
9548     
9549      /**
9550      * Mark this field as valid
9551      */
9552     markValid : function()
9553     {
9554         if(!this.el  || this.preventMark){ // not rendered...
9555             return;
9556         }
9557         
9558         this.el.removeClass([this.invalidClass, this.validClass]);
9559         
9560         var feedback = this.el.select('.form-control-feedback', true).first();
9561             
9562         if(feedback){
9563             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9564         }
9565         
9566         if(this.indicator){
9567             this.indicator.removeClass('visible');
9568             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9569         }
9570         
9571         if(this.disabled){
9572             return;
9573         }
9574         
9575         if(this.allowBlank && !this.getRawValue().length){
9576             return;
9577         }
9578         
9579         this.el.addClass(this.validClass);
9580         
9581         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9588             }
9589             
9590         }
9591         
9592         this.fireEvent('valid', this);
9593     },
9594     
9595      /**
9596      * Mark this field as invalid
9597      * @param {String} msg The validation message
9598      */
9599     markInvalid : function(msg)
9600     {
9601         if(!this.el  || this.preventMark){ // not rendered
9602             return;
9603         }
9604         
9605         this.el.removeClass([this.invalidClass, this.validClass]);
9606         
9607         var feedback = this.el.select('.form-control-feedback', true).first();
9608             
9609         if(feedback){
9610             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9611         }
9612
9613         if(this.disabled){
9614             return;
9615         }
9616         
9617         if(this.allowBlank && !this.getRawValue().length){
9618             return;
9619         }
9620         
9621         if(this.indicator){
9622             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9623             this.indicator.addClass('visible');
9624         }
9625         
9626         this.el.addClass(this.invalidClass);
9627         
9628         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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                 if(this.getValue().length || this.forceFeedback){
9636                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9637                 }
9638                 
9639             }
9640             
9641         }
9642         
9643         this.fireEvent('invalid', this, msg);
9644     },
9645     // private
9646     SafariOnKeyDown : function(event)
9647     {
9648         // this is a workaround for a password hang bug on chrome/ webkit.
9649         if (this.inputEl().dom.type != 'password') {
9650             return;
9651         }
9652         
9653         var isSelectAll = false;
9654         
9655         if(this.inputEl().dom.selectionEnd > 0){
9656             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9657         }
9658         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9659             event.preventDefault();
9660             this.setValue('');
9661             return;
9662         }
9663         
9664         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9665             
9666             event.preventDefault();
9667             // this is very hacky as keydown always get's upper case.
9668             //
9669             var cc = String.fromCharCode(event.getCharCode());
9670             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9671             
9672         }
9673     },
9674     adjustWidth : function(tag, w){
9675         tag = tag.toLowerCase();
9676         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9677             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9678                 if(tag == 'input'){
9679                     return w + 2;
9680                 }
9681                 if(tag == 'textarea'){
9682                     return w-2;
9683                 }
9684             }else if(Roo.isOpera){
9685                 if(tag == 'input'){
9686                     return w + 2;
9687                 }
9688                 if(tag == 'textarea'){
9689                     return w-2;
9690                 }
9691             }
9692         }
9693         return w;
9694     },
9695     
9696     setFieldLabel : function(v)
9697     {
9698         if(!this.rendered){
9699             return;
9700         }
9701         
9702         if(this.indicator){
9703             var ar = this.el.select('label > span',true);
9704             
9705             if (ar.elements.length) {
9706                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9707                 this.fieldLabel = v;
9708                 return;
9709             }
9710             
9711             var br = this.el.select('label',true);
9712             
9713             if(br.elements.length) {
9714                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9715                 this.fieldLabel = v;
9716                 return;
9717             }
9718             
9719             Roo.log('Cannot Found any of label > span || label in input');
9720             return;
9721         }
9722         
9723         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9724         this.fieldLabel = v;
9725         
9726         
9727     }
9728 });
9729
9730  
9731 /*
9732  * - LGPL
9733  *
9734  * Input
9735  * 
9736  */
9737
9738 /**
9739  * @class Roo.bootstrap.TextArea
9740  * @extends Roo.bootstrap.Input
9741  * Bootstrap TextArea class
9742  * @cfg {Number} cols Specifies the visible width of a text area
9743  * @cfg {Number} rows Specifies the visible number of lines in a text area
9744  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9745  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9746  * @cfg {string} html text
9747  * 
9748  * @constructor
9749  * Create a new TextArea
9750  * @param {Object} config The config object
9751  */
9752
9753 Roo.bootstrap.TextArea = function(config){
9754     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9755    
9756 };
9757
9758 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9759      
9760     cols : false,
9761     rows : 5,
9762     readOnly : false,
9763     warp : 'soft',
9764     resize : false,
9765     value: false,
9766     html: false,
9767     
9768     getAutoCreate : function(){
9769         
9770         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9771         
9772         var id = Roo.id();
9773         
9774         var cfg = {};
9775         
9776         if(this.inputType != 'hidden'){
9777             cfg.cls = 'form-group' //input-group
9778         }
9779         
9780         var input =  {
9781             tag: 'textarea',
9782             id : id,
9783             warp : this.warp,
9784             rows : this.rows,
9785             value : this.value || '',
9786             html: this.html || '',
9787             cls : 'form-control',
9788             placeholder : this.placeholder || '' 
9789             
9790         };
9791         
9792         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9793             input.maxLength = this.maxLength;
9794         }
9795         
9796         if(this.resize){
9797             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9798         }
9799         
9800         if(this.cols){
9801             input.cols = this.cols;
9802         }
9803         
9804         if (this.readOnly) {
9805             input.readonly = true;
9806         }
9807         
9808         if (this.name) {
9809             input.name = this.name;
9810         }
9811         
9812         if (this.size) {
9813             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9814         }
9815         
9816         var settings=this;
9817         ['xs','sm','md','lg'].map(function(size){
9818             if (settings[size]) {
9819                 cfg.cls += ' col-' + size + '-' + settings[size];
9820             }
9821         });
9822         
9823         var inputblock = input;
9824         
9825         if(this.hasFeedback && !this.allowBlank){
9826             
9827             var feedback = {
9828                 tag: 'span',
9829                 cls: 'glyphicon form-control-feedback'
9830             };
9831
9832             inputblock = {
9833                 cls : 'has-feedback',
9834                 cn :  [
9835                     input,
9836                     feedback
9837                 ] 
9838             };  
9839         }
9840         
9841         
9842         if (this.before || this.after) {
9843             
9844             inputblock = {
9845                 cls : 'input-group',
9846                 cn :  [] 
9847             };
9848             if (this.before) {
9849                 inputblock.cn.push({
9850                     tag :'span',
9851                     cls : 'input-group-addon',
9852                     html : this.before
9853                 });
9854             }
9855             
9856             inputblock.cn.push(input);
9857             
9858             if(this.hasFeedback && !this.allowBlank){
9859                 inputblock.cls += ' has-feedback';
9860                 inputblock.cn.push(feedback);
9861             }
9862             
9863             if (this.after) {
9864                 inputblock.cn.push({
9865                     tag :'span',
9866                     cls : 'input-group-addon',
9867                     html : this.after
9868                 });
9869             }
9870             
9871         }
9872         
9873         if (align ==='left' && this.fieldLabel.length) {
9874             cfg.cn = [
9875                 {
9876                     tag: 'label',
9877                     'for' :  id,
9878                     cls : 'control-label',
9879                     html : this.fieldLabel
9880                 },
9881                 {
9882                     cls : "",
9883                     cn: [
9884                         inputblock
9885                     ]
9886                 }
9887
9888             ];
9889             
9890             if(this.labelWidth > 12){
9891                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9892             }
9893
9894             if(this.labelWidth < 13 && this.labelmd == 0){
9895                 this.labelmd = this.labelWidth;
9896             }
9897
9898             if(this.labellg > 0){
9899                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9900                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9901             }
9902
9903             if(this.labelmd > 0){
9904                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9905                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9906             }
9907
9908             if(this.labelsm > 0){
9909                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9910                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9911             }
9912
9913             if(this.labelxs > 0){
9914                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9915                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9916             }
9917             
9918         } else if ( this.fieldLabel.length) {
9919             cfg.cn = [
9920
9921                {
9922                    tag: 'label',
9923                    //cls : 'input-group-addon',
9924                    html : this.fieldLabel
9925
9926                },
9927
9928                inputblock
9929
9930            ];
9931
9932         } else {
9933
9934             cfg.cn = [
9935
9936                 inputblock
9937
9938             ];
9939                 
9940         }
9941         
9942         if (this.disabled) {
9943             input.disabled=true;
9944         }
9945         
9946         return cfg;
9947         
9948     },
9949     /**
9950      * return the real textarea element.
9951      */
9952     inputEl: function ()
9953     {
9954         return this.el.select('textarea.form-control',true).first();
9955     },
9956     
9957     /**
9958      * Clear any invalid styles/messages for this field
9959      */
9960     clearInvalid : function()
9961     {
9962         
9963         if(!this.el || this.preventMark){ // not rendered
9964             return;
9965         }
9966         
9967         var label = this.el.select('label', true).first();
9968         var icon = this.el.select('i.fa-star', true).first();
9969         
9970         if(label && icon){
9971             icon.remove();
9972         }
9973         
9974         this.el.removeClass(this.invalidClass);
9975         
9976         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9977             
9978             var feedback = this.el.select('.form-control-feedback', true).first();
9979             
9980             if(feedback){
9981                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9982             }
9983             
9984         }
9985         
9986         this.fireEvent('valid', this);
9987     },
9988     
9989      /**
9990      * Mark this field as valid
9991      */
9992     markValid : function()
9993     {
9994         if(!this.el  || this.preventMark){ // not rendered
9995             return;
9996         }
9997         
9998         this.el.removeClass([this.invalidClass, this.validClass]);
9999         
10000         var feedback = this.el.select('.form-control-feedback', true).first();
10001             
10002         if(feedback){
10003             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10004         }
10005
10006         if(this.disabled || this.allowBlank){
10007             return;
10008         }
10009         
10010         var label = this.el.select('label', true).first();
10011         var icon = this.el.select('i.fa-star', true).first();
10012         
10013         if(label && icon){
10014             icon.remove();
10015         }
10016         
10017         this.el.addClass(this.validClass);
10018         
10019         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10020             
10021             var feedback = this.el.select('.form-control-feedback', true).first();
10022             
10023             if(feedback){
10024                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10025                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10026             }
10027             
10028         }
10029         
10030         this.fireEvent('valid', this);
10031     },
10032     
10033      /**
10034      * Mark this field as invalid
10035      * @param {String} msg The validation message
10036      */
10037     markInvalid : function(msg)
10038     {
10039         if(!this.el  || this.preventMark){ // not rendered
10040             return;
10041         }
10042         
10043         this.el.removeClass([this.invalidClass, this.validClass]);
10044         
10045         var feedback = this.el.select('.form-control-feedback', true).first();
10046             
10047         if(feedback){
10048             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10049         }
10050
10051         if(this.disabled || this.allowBlank){
10052             return;
10053         }
10054         
10055         var label = this.el.select('label', true).first();
10056         var icon = this.el.select('i.fa-star', true).first();
10057         
10058         if(!this.getValue().length && label && !icon){
10059             this.el.createChild({
10060                 tag : 'i',
10061                 cls : 'text-danger fa fa-lg fa-star',
10062                 tooltip : 'This field is required',
10063                 style : 'margin-right:5px;'
10064             }, label, true);
10065         }
10066
10067         this.el.addClass(this.invalidClass);
10068         
10069         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10070             
10071             var feedback = this.el.select('.form-control-feedback', true).first();
10072             
10073             if(feedback){
10074                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10075                 
10076                 if(this.getValue().length || this.forceFeedback){
10077                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10078                 }
10079                 
10080             }
10081             
10082         }
10083         
10084         this.fireEvent('invalid', this, msg);
10085     }
10086 });
10087
10088  
10089 /*
10090  * - LGPL
10091  *
10092  * trigger field - base class for combo..
10093  * 
10094  */
10095  
10096 /**
10097  * @class Roo.bootstrap.TriggerField
10098  * @extends Roo.bootstrap.Input
10099  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10100  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10101  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10102  * for which you can provide a custom implementation.  For example:
10103  * <pre><code>
10104 var trigger = new Roo.bootstrap.TriggerField();
10105 trigger.onTriggerClick = myTriggerFn;
10106 trigger.applyTo('my-field');
10107 </code></pre>
10108  *
10109  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10110  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10111  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10112  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10113  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10114
10115  * @constructor
10116  * Create a new TriggerField.
10117  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10118  * to the base TextField)
10119  */
10120 Roo.bootstrap.TriggerField = function(config){
10121     this.mimicing = false;
10122     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10123 };
10124
10125 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10126     /**
10127      * @cfg {String} triggerClass A CSS class to apply to the trigger
10128      */
10129      /**
10130      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10131      */
10132     hideTrigger:false,
10133
10134     /**
10135      * @cfg {Boolean} removable (true|false) special filter default false
10136      */
10137     removable : false,
10138     
10139     /** @cfg {Boolean} grow @hide */
10140     /** @cfg {Number} growMin @hide */
10141     /** @cfg {Number} growMax @hide */
10142
10143     /**
10144      * @hide 
10145      * @method
10146      */
10147     autoSize: Roo.emptyFn,
10148     // private
10149     monitorTab : true,
10150     // private
10151     deferHeight : true,
10152
10153     
10154     actionMode : 'wrap',
10155     
10156     caret : false,
10157     
10158     
10159     getAutoCreate : function(){
10160        
10161         var align = this.labelAlign || this.parentLabelAlign();
10162         
10163         var id = Roo.id();
10164         
10165         var cfg = {
10166             cls: 'form-group' //input-group
10167         };
10168         
10169         
10170         var input =  {
10171             tag: 'input',
10172             id : id,
10173             type : this.inputType,
10174             cls : 'form-control',
10175             autocomplete: 'new-password',
10176             placeholder : this.placeholder || '' 
10177             
10178         };
10179         if (this.name) {
10180             input.name = this.name;
10181         }
10182         if (this.size) {
10183             input.cls += ' input-' + this.size;
10184         }
10185         
10186         if (this.disabled) {
10187             input.disabled=true;
10188         }
10189         
10190         var inputblock = input;
10191         
10192         if(this.hasFeedback && !this.allowBlank){
10193             
10194             var feedback = {
10195                 tag: 'span',
10196                 cls: 'glyphicon form-control-feedback'
10197             };
10198             
10199             if(this.removable && !this.editable && !this.tickable){
10200                 inputblock = {
10201                     cls : 'has-feedback',
10202                     cn :  [
10203                         inputblock,
10204                         {
10205                             tag: 'button',
10206                             html : 'x',
10207                             cls : 'roo-combo-removable-btn close'
10208                         },
10209                         feedback
10210                     ] 
10211                 };
10212             } else {
10213                 inputblock = {
10214                     cls : 'has-feedback',
10215                     cn :  [
10216                         inputblock,
10217                         feedback
10218                     ] 
10219                 };
10220             }
10221
10222         } else {
10223             if(this.removable && !this.editable && !this.tickable){
10224                 inputblock = {
10225                     cls : 'roo-removable',
10226                     cn :  [
10227                         inputblock,
10228                         {
10229                             tag: 'button',
10230                             html : 'x',
10231                             cls : 'roo-combo-removable-btn close'
10232                         }
10233                     ] 
10234                 };
10235             }
10236         }
10237         
10238         if (this.before || this.after) {
10239             
10240             inputblock = {
10241                 cls : 'input-group',
10242                 cn :  [] 
10243             };
10244             if (this.before) {
10245                 inputblock.cn.push({
10246                     tag :'span',
10247                     cls : 'input-group-addon',
10248                     html : this.before
10249                 });
10250             }
10251             
10252             inputblock.cn.push(input);
10253             
10254             if(this.hasFeedback && !this.allowBlank){
10255                 inputblock.cls += ' has-feedback';
10256                 inputblock.cn.push(feedback);
10257             }
10258             
10259             if (this.after) {
10260                 inputblock.cn.push({
10261                     tag :'span',
10262                     cls : 'input-group-addon',
10263                     html : this.after
10264                 });
10265             }
10266             
10267         };
10268         
10269         var box = {
10270             tag: 'div',
10271             cn: [
10272                 {
10273                     tag: 'input',
10274                     type : 'hidden',
10275                     cls: 'form-hidden-field'
10276                 },
10277                 inputblock
10278             ]
10279             
10280         };
10281         
10282         if(this.multiple){
10283             box = {
10284                 tag: 'div',
10285                 cn: [
10286                     {
10287                         tag: 'input',
10288                         type : 'hidden',
10289                         cls: 'form-hidden-field'
10290                     },
10291                     {
10292                         tag: 'ul',
10293                         cls: 'roo-select2-choices',
10294                         cn:[
10295                             {
10296                                 tag: 'li',
10297                                 cls: 'roo-select2-search-field',
10298                                 cn: [
10299
10300                                     inputblock
10301                                 ]
10302                             }
10303                         ]
10304                     }
10305                 ]
10306             }
10307         };
10308         
10309         var combobox = {
10310             cls: 'roo-select2-container input-group',
10311             cn: [
10312                 box
10313 //                {
10314 //                    tag: 'ul',
10315 //                    cls: 'typeahead typeahead-long dropdown-menu',
10316 //                    style: 'display:none'
10317 //                }
10318             ]
10319         };
10320         
10321         if(!this.multiple && this.showToggleBtn){
10322             
10323             var caret = {
10324                         tag: 'span',
10325                         cls: 'caret'
10326              };
10327             if (this.caret != false) {
10328                 caret = {
10329                      tag: 'i',
10330                      cls: 'fa fa-' + this.caret
10331                 };
10332                 
10333             }
10334             
10335             combobox.cn.push({
10336                 tag :'span',
10337                 cls : 'input-group-addon btn dropdown-toggle',
10338                 cn : [
10339                     caret,
10340                     {
10341                         tag: 'span',
10342                         cls: 'combobox-clear',
10343                         cn  : [
10344                             {
10345                                 tag : 'i',
10346                                 cls: 'icon-remove'
10347                             }
10348                         ]
10349                     }
10350                 ]
10351
10352             })
10353         }
10354         
10355         if(this.multiple){
10356             combobox.cls += ' roo-select2-container-multi';
10357         }
10358         
10359         if (align ==='left' && this.fieldLabel.length) {
10360             
10361             cfg.cls += ' roo-form-group-label-left';
10362
10363             cfg.cn = [
10364                 {
10365                     tag : 'i',
10366                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10367                     tooltip : 'This field is required'
10368                 },
10369                 {
10370                     tag: 'label',
10371                     'for' :  id,
10372                     cls : 'control-label',
10373                     html : this.fieldLabel
10374
10375                 },
10376                 {
10377                     cls : "", 
10378                     cn: [
10379                         combobox
10380                     ]
10381                 }
10382
10383             ];
10384             
10385             var labelCfg = cfg.cn[1];
10386             var contentCfg = cfg.cn[2];
10387             
10388             if(this.indicatorpos == 'right'){
10389                 cfg.cn = [
10390                     {
10391                         tag: 'label',
10392                         'for' :  id,
10393                         cls : 'control-label',
10394                         cn : [
10395                             {
10396                                 tag : 'span',
10397                                 html : this.fieldLabel
10398                             },
10399                             {
10400                                 tag : 'i',
10401                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10402                                 tooltip : 'This field is required'
10403                             }
10404                         ]
10405                     },
10406                     {
10407                         cls : "", 
10408                         cn: [
10409                             combobox
10410                         ]
10411                     }
10412
10413                 ];
10414                 
10415                 labelCfg = cfg.cn[0];
10416                 contentCfg = cfg.cn[1];
10417             }
10418             
10419             if(this.labelWidth > 12){
10420                 labelCfg.style = "width: " + this.labelWidth + 'px';
10421             }
10422             
10423             if(this.labelWidth < 13 && this.labelmd == 0){
10424                 this.labelmd = this.labelWidth;
10425             }
10426             
10427             if(this.labellg > 0){
10428                 labelCfg.cls += ' col-lg-' + this.labellg;
10429                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10430             }
10431             
10432             if(this.labelmd > 0){
10433                 labelCfg.cls += ' col-md-' + this.labelmd;
10434                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10435             }
10436             
10437             if(this.labelsm > 0){
10438                 labelCfg.cls += ' col-sm-' + this.labelsm;
10439                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10440             }
10441             
10442             if(this.labelxs > 0){
10443                 labelCfg.cls += ' col-xs-' + this.labelxs;
10444                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10445             }
10446             
10447         } else if ( this.fieldLabel.length) {
10448 //                Roo.log(" label");
10449             cfg.cn = [
10450                 {
10451                    tag : 'i',
10452                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10453                    tooltip : 'This field is required'
10454                },
10455                {
10456                    tag: 'label',
10457                    //cls : 'input-group-addon',
10458                    html : this.fieldLabel
10459
10460                },
10461
10462                combobox
10463
10464             ];
10465             
10466             if(this.indicatorpos == 'right'){
10467                 
10468                 cfg.cn = [
10469                     {
10470                        tag: 'label',
10471                        cn : [
10472                            {
10473                                tag : 'span',
10474                                html : this.fieldLabel
10475                            },
10476                            {
10477                               tag : 'i',
10478                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10479                               tooltip : 'This field is required'
10480                            }
10481                        ]
10482
10483                     },
10484                     combobox
10485
10486                 ];
10487
10488             }
10489
10490         } else {
10491             
10492 //                Roo.log(" no label && no align");
10493                 cfg = combobox
10494                      
10495                 
10496         }
10497         
10498         var settings=this;
10499         ['xs','sm','md','lg'].map(function(size){
10500             if (settings[size]) {
10501                 cfg.cls += ' col-' + size + '-' + settings[size];
10502             }
10503         });
10504         
10505         return cfg;
10506         
10507     },
10508     
10509     
10510     
10511     // private
10512     onResize : function(w, h){
10513 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10514 //        if(typeof w == 'number'){
10515 //            var x = w - this.trigger.getWidth();
10516 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10517 //            this.trigger.setStyle('left', x+'px');
10518 //        }
10519     },
10520
10521     // private
10522     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10523
10524     // private
10525     getResizeEl : function(){
10526         return this.inputEl();
10527     },
10528
10529     // private
10530     getPositionEl : function(){
10531         return this.inputEl();
10532     },
10533
10534     // private
10535     alignErrorIcon : function(){
10536         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10537     },
10538
10539     // private
10540     initEvents : function(){
10541         
10542         this.createList();
10543         
10544         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10545         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10546         if(!this.multiple && this.showToggleBtn){
10547             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10548             if(this.hideTrigger){
10549                 this.trigger.setDisplayed(false);
10550             }
10551             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10552         }
10553         
10554         if(this.multiple){
10555             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10556         }
10557         
10558         if(this.removable && !this.editable && !this.tickable){
10559             var close = this.closeTriggerEl();
10560             
10561             if(close){
10562                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10563                 close.on('click', this.removeBtnClick, this, close);
10564             }
10565         }
10566         
10567         //this.trigger.addClassOnOver('x-form-trigger-over');
10568         //this.trigger.addClassOnClick('x-form-trigger-click');
10569         
10570         //if(!this.width){
10571         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10572         //}
10573     },
10574     
10575     closeTriggerEl : function()
10576     {
10577         var close = this.el.select('.roo-combo-removable-btn', true).first();
10578         return close ? close : false;
10579     },
10580     
10581     removeBtnClick : function(e, h, el)
10582     {
10583         e.preventDefault();
10584         
10585         if(this.fireEvent("remove", this) !== false){
10586             this.reset();
10587             this.fireEvent("afterremove", this)
10588         }
10589     },
10590     
10591     createList : function()
10592     {
10593         this.list = Roo.get(document.body).createChild({
10594             tag: 'ul',
10595             cls: 'typeahead typeahead-long dropdown-menu',
10596             style: 'display:none'
10597         });
10598         
10599         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10600         
10601     },
10602
10603     // private
10604     initTrigger : function(){
10605        
10606     },
10607
10608     // private
10609     onDestroy : function(){
10610         if(this.trigger){
10611             this.trigger.removeAllListeners();
10612           //  this.trigger.remove();
10613         }
10614         //if(this.wrap){
10615         //    this.wrap.remove();
10616         //}
10617         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10618     },
10619
10620     // private
10621     onFocus : function(){
10622         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10623         /*
10624         if(!this.mimicing){
10625             this.wrap.addClass('x-trigger-wrap-focus');
10626             this.mimicing = true;
10627             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10628             if(this.monitorTab){
10629                 this.el.on("keydown", this.checkTab, this);
10630             }
10631         }
10632         */
10633     },
10634
10635     // private
10636     checkTab : function(e){
10637         if(e.getKey() == e.TAB){
10638             this.triggerBlur();
10639         }
10640     },
10641
10642     // private
10643     onBlur : function(){
10644         // do nothing
10645     },
10646
10647     // private
10648     mimicBlur : function(e, t){
10649         /*
10650         if(!this.wrap.contains(t) && this.validateBlur()){
10651             this.triggerBlur();
10652         }
10653         */
10654     },
10655
10656     // private
10657     triggerBlur : function(){
10658         this.mimicing = false;
10659         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10660         if(this.monitorTab){
10661             this.el.un("keydown", this.checkTab, this);
10662         }
10663         //this.wrap.removeClass('x-trigger-wrap-focus');
10664         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10665     },
10666
10667     // private
10668     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10669     validateBlur : function(e, t){
10670         return true;
10671     },
10672
10673     // private
10674     onDisable : function(){
10675         this.inputEl().dom.disabled = true;
10676         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10677         //if(this.wrap){
10678         //    this.wrap.addClass('x-item-disabled');
10679         //}
10680     },
10681
10682     // private
10683     onEnable : function(){
10684         this.inputEl().dom.disabled = false;
10685         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10686         //if(this.wrap){
10687         //    this.el.removeClass('x-item-disabled');
10688         //}
10689     },
10690
10691     // private
10692     onShow : function(){
10693         var ae = this.getActionEl();
10694         
10695         if(ae){
10696             ae.dom.style.display = '';
10697             ae.dom.style.visibility = 'visible';
10698         }
10699     },
10700
10701     // private
10702     
10703     onHide : function(){
10704         var ae = this.getActionEl();
10705         ae.dom.style.display = 'none';
10706     },
10707
10708     /**
10709      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10710      * by an implementing function.
10711      * @method
10712      * @param {EventObject} e
10713      */
10714     onTriggerClick : Roo.emptyFn
10715 });
10716  /*
10717  * Based on:
10718  * Ext JS Library 1.1.1
10719  * Copyright(c) 2006-2007, Ext JS, LLC.
10720  *
10721  * Originally Released Under LGPL - original licence link has changed is not relivant.
10722  *
10723  * Fork - LGPL
10724  * <script type="text/javascript">
10725  */
10726
10727
10728 /**
10729  * @class Roo.data.SortTypes
10730  * @singleton
10731  * Defines the default sorting (casting?) comparison functions used when sorting data.
10732  */
10733 Roo.data.SortTypes = {
10734     /**
10735      * Default sort that does nothing
10736      * @param {Mixed} s The value being converted
10737      * @return {Mixed} The comparison value
10738      */
10739     none : function(s){
10740         return s;
10741     },
10742     
10743     /**
10744      * The regular expression used to strip tags
10745      * @type {RegExp}
10746      * @property
10747      */
10748     stripTagsRE : /<\/?[^>]+>/gi,
10749     
10750     /**
10751      * Strips all HTML tags to sort on text only
10752      * @param {Mixed} s The value being converted
10753      * @return {String} The comparison value
10754      */
10755     asText : function(s){
10756         return String(s).replace(this.stripTagsRE, "");
10757     },
10758     
10759     /**
10760      * Strips all HTML tags to sort on text only - Case insensitive
10761      * @param {Mixed} s The value being converted
10762      * @return {String} The comparison value
10763      */
10764     asUCText : function(s){
10765         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10766     },
10767     
10768     /**
10769      * Case insensitive string
10770      * @param {Mixed} s The value being converted
10771      * @return {String} The comparison value
10772      */
10773     asUCString : function(s) {
10774         return String(s).toUpperCase();
10775     },
10776     
10777     /**
10778      * Date sorting
10779      * @param {Mixed} s The value being converted
10780      * @return {Number} The comparison value
10781      */
10782     asDate : function(s) {
10783         if(!s){
10784             return 0;
10785         }
10786         if(s instanceof Date){
10787             return s.getTime();
10788         }
10789         return Date.parse(String(s));
10790     },
10791     
10792     /**
10793      * Float sorting
10794      * @param {Mixed} s The value being converted
10795      * @return {Float} The comparison value
10796      */
10797     asFloat : function(s) {
10798         var val = parseFloat(String(s).replace(/,/g, ""));
10799         if(isNaN(val)) {
10800             val = 0;
10801         }
10802         return val;
10803     },
10804     
10805     /**
10806      * Integer sorting
10807      * @param {Mixed} s The value being converted
10808      * @return {Number} The comparison value
10809      */
10810     asInt : function(s) {
10811         var val = parseInt(String(s).replace(/,/g, ""));
10812         if(isNaN(val)) {
10813             val = 0;
10814         }
10815         return val;
10816     }
10817 };/*
10818  * Based on:
10819  * Ext JS Library 1.1.1
10820  * Copyright(c) 2006-2007, Ext JS, LLC.
10821  *
10822  * Originally Released Under LGPL - original licence link has changed is not relivant.
10823  *
10824  * Fork - LGPL
10825  * <script type="text/javascript">
10826  */
10827
10828 /**
10829 * @class Roo.data.Record
10830  * Instances of this class encapsulate both record <em>definition</em> information, and record
10831  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10832  * to access Records cached in an {@link Roo.data.Store} object.<br>
10833  * <p>
10834  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10835  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10836  * objects.<br>
10837  * <p>
10838  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10839  * @constructor
10840  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10841  * {@link #create}. The parameters are the same.
10842  * @param {Array} data An associative Array of data values keyed by the field name.
10843  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10844  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10845  * not specified an integer id is generated.
10846  */
10847 Roo.data.Record = function(data, id){
10848     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10849     this.data = data;
10850 };
10851
10852 /**
10853  * Generate a constructor for a specific record layout.
10854  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10855  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10856  * Each field definition object may contain the following properties: <ul>
10857  * <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,
10858  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10859  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10860  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10861  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10862  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10863  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10864  * this may be omitted.</p></li>
10865  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10866  * <ul><li>auto (Default, implies no conversion)</li>
10867  * <li>string</li>
10868  * <li>int</li>
10869  * <li>float</li>
10870  * <li>boolean</li>
10871  * <li>date</li></ul></p></li>
10872  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10873  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10874  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10875  * by the Reader into an object that will be stored in the Record. It is passed the
10876  * following parameters:<ul>
10877  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10878  * </ul></p></li>
10879  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10880  * </ul>
10881  * <br>usage:<br><pre><code>
10882 var TopicRecord = Roo.data.Record.create(
10883     {name: 'title', mapping: 'topic_title'},
10884     {name: 'author', mapping: 'username'},
10885     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10886     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10887     {name: 'lastPoster', mapping: 'user2'},
10888     {name: 'excerpt', mapping: 'post_text'}
10889 );
10890
10891 var myNewRecord = new TopicRecord({
10892     title: 'Do my job please',
10893     author: 'noobie',
10894     totalPosts: 1,
10895     lastPost: new Date(),
10896     lastPoster: 'Animal',
10897     excerpt: 'No way dude!'
10898 });
10899 myStore.add(myNewRecord);
10900 </code></pre>
10901  * @method create
10902  * @static
10903  */
10904 Roo.data.Record.create = function(o){
10905     var f = function(){
10906         f.superclass.constructor.apply(this, arguments);
10907     };
10908     Roo.extend(f, Roo.data.Record);
10909     var p = f.prototype;
10910     p.fields = new Roo.util.MixedCollection(false, function(field){
10911         return field.name;
10912     });
10913     for(var i = 0, len = o.length; i < len; i++){
10914         p.fields.add(new Roo.data.Field(o[i]));
10915     }
10916     f.getField = function(name){
10917         return p.fields.get(name);  
10918     };
10919     return f;
10920 };
10921
10922 Roo.data.Record.AUTO_ID = 1000;
10923 Roo.data.Record.EDIT = 'edit';
10924 Roo.data.Record.REJECT = 'reject';
10925 Roo.data.Record.COMMIT = 'commit';
10926
10927 Roo.data.Record.prototype = {
10928     /**
10929      * Readonly flag - true if this record has been modified.
10930      * @type Boolean
10931      */
10932     dirty : false,
10933     editing : false,
10934     error: null,
10935     modified: null,
10936
10937     // private
10938     join : function(store){
10939         this.store = store;
10940     },
10941
10942     /**
10943      * Set the named field to the specified value.
10944      * @param {String} name The name of the field to set.
10945      * @param {Object} value The value to set the field to.
10946      */
10947     set : function(name, value){
10948         if(this.data[name] == value){
10949             return;
10950         }
10951         this.dirty = true;
10952         if(!this.modified){
10953             this.modified = {};
10954         }
10955         if(typeof this.modified[name] == 'undefined'){
10956             this.modified[name] = this.data[name];
10957         }
10958         this.data[name] = value;
10959         if(!this.editing && this.store){
10960             this.store.afterEdit(this);
10961         }       
10962     },
10963
10964     /**
10965      * Get the value of the named field.
10966      * @param {String} name The name of the field to get the value of.
10967      * @return {Object} The value of the field.
10968      */
10969     get : function(name){
10970         return this.data[name]; 
10971     },
10972
10973     // private
10974     beginEdit : function(){
10975         this.editing = true;
10976         this.modified = {}; 
10977     },
10978
10979     // private
10980     cancelEdit : function(){
10981         this.editing = false;
10982         delete this.modified;
10983     },
10984
10985     // private
10986     endEdit : function(){
10987         this.editing = false;
10988         if(this.dirty && this.store){
10989             this.store.afterEdit(this);
10990         }
10991     },
10992
10993     /**
10994      * Usually called by the {@link Roo.data.Store} which owns the Record.
10995      * Rejects all changes made to the Record since either creation, or the last commit operation.
10996      * Modified fields are reverted to their original values.
10997      * <p>
10998      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10999      * of reject operations.
11000      */
11001     reject : function(){
11002         var m = this.modified;
11003         for(var n in m){
11004             if(typeof m[n] != "function"){
11005                 this.data[n] = m[n];
11006             }
11007         }
11008         this.dirty = false;
11009         delete this.modified;
11010         this.editing = false;
11011         if(this.store){
11012             this.store.afterReject(this);
11013         }
11014     },
11015
11016     /**
11017      * Usually called by the {@link Roo.data.Store} which owns the Record.
11018      * Commits all changes made to the Record since either creation, or the last commit operation.
11019      * <p>
11020      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11021      * of commit operations.
11022      */
11023     commit : function(){
11024         this.dirty = false;
11025         delete this.modified;
11026         this.editing = false;
11027         if(this.store){
11028             this.store.afterCommit(this);
11029         }
11030     },
11031
11032     // private
11033     hasError : function(){
11034         return this.error != null;
11035     },
11036
11037     // private
11038     clearError : function(){
11039         this.error = null;
11040     },
11041
11042     /**
11043      * Creates a copy of this record.
11044      * @param {String} id (optional) A new record id if you don't want to use this record's id
11045      * @return {Record}
11046      */
11047     copy : function(newId) {
11048         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11049     }
11050 };/*
11051  * Based on:
11052  * Ext JS Library 1.1.1
11053  * Copyright(c) 2006-2007, Ext JS, LLC.
11054  *
11055  * Originally Released Under LGPL - original licence link has changed is not relivant.
11056  *
11057  * Fork - LGPL
11058  * <script type="text/javascript">
11059  */
11060
11061
11062
11063 /**
11064  * @class Roo.data.Store
11065  * @extends Roo.util.Observable
11066  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11067  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11068  * <p>
11069  * 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
11070  * has no knowledge of the format of the data returned by the Proxy.<br>
11071  * <p>
11072  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11073  * instances from the data object. These records are cached and made available through accessor functions.
11074  * @constructor
11075  * Creates a new Store.
11076  * @param {Object} config A config object containing the objects needed for the Store to access data,
11077  * and read the data into Records.
11078  */
11079 Roo.data.Store = function(config){
11080     this.data = new Roo.util.MixedCollection(false);
11081     this.data.getKey = function(o){
11082         return o.id;
11083     };
11084     this.baseParams = {};
11085     // private
11086     this.paramNames = {
11087         "start" : "start",
11088         "limit" : "limit",
11089         "sort" : "sort",
11090         "dir" : "dir",
11091         "multisort" : "_multisort"
11092     };
11093
11094     if(config && config.data){
11095         this.inlineData = config.data;
11096         delete config.data;
11097     }
11098
11099     Roo.apply(this, config);
11100     
11101     if(this.reader){ // reader passed
11102         this.reader = Roo.factory(this.reader, Roo.data);
11103         this.reader.xmodule = this.xmodule || false;
11104         if(!this.recordType){
11105             this.recordType = this.reader.recordType;
11106         }
11107         if(this.reader.onMetaChange){
11108             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11109         }
11110     }
11111
11112     if(this.recordType){
11113         this.fields = this.recordType.prototype.fields;
11114     }
11115     this.modified = [];
11116
11117     this.addEvents({
11118         /**
11119          * @event datachanged
11120          * Fires when the data cache has changed, and a widget which is using this Store
11121          * as a Record cache should refresh its view.
11122          * @param {Store} this
11123          */
11124         datachanged : true,
11125         /**
11126          * @event metachange
11127          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11128          * @param {Store} this
11129          * @param {Object} meta The JSON metadata
11130          */
11131         metachange : true,
11132         /**
11133          * @event add
11134          * Fires when Records have been added to the Store
11135          * @param {Store} this
11136          * @param {Roo.data.Record[]} records The array of Records added
11137          * @param {Number} index The index at which the record(s) were added
11138          */
11139         add : true,
11140         /**
11141          * @event remove
11142          * Fires when a Record has been removed from the Store
11143          * @param {Store} this
11144          * @param {Roo.data.Record} record The Record that was removed
11145          * @param {Number} index The index at which the record was removed
11146          */
11147         remove : true,
11148         /**
11149          * @event update
11150          * Fires when a Record has been updated
11151          * @param {Store} this
11152          * @param {Roo.data.Record} record The Record that was updated
11153          * @param {String} operation The update operation being performed.  Value may be one of:
11154          * <pre><code>
11155  Roo.data.Record.EDIT
11156  Roo.data.Record.REJECT
11157  Roo.data.Record.COMMIT
11158          * </code></pre>
11159          */
11160         update : true,
11161         /**
11162          * @event clear
11163          * Fires when the data cache has been cleared.
11164          * @param {Store} this
11165          */
11166         clear : true,
11167         /**
11168          * @event beforeload
11169          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11170          * the load action will be canceled.
11171          * @param {Store} this
11172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11173          */
11174         beforeload : true,
11175         /**
11176          * @event beforeloadadd
11177          * Fires after a new set of Records has been loaded.
11178          * @param {Store} this
11179          * @param {Roo.data.Record[]} records The Records that were loaded
11180          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11181          */
11182         beforeloadadd : true,
11183         /**
11184          * @event load
11185          * Fires after a new set of Records has been loaded, before they are added to the store.
11186          * @param {Store} this
11187          * @param {Roo.data.Record[]} records The Records that were loaded
11188          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11189          * @params {Object} return from reader
11190          */
11191         load : true,
11192         /**
11193          * @event loadexception
11194          * Fires if an exception occurs in the Proxy during loading.
11195          * Called with the signature of the Proxy's "loadexception" event.
11196          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11197          * 
11198          * @param {Proxy} 
11199          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11200          * @param {Object} load options 
11201          * @param {Object} jsonData from your request (normally this contains the Exception)
11202          */
11203         loadexception : true
11204     });
11205     
11206     if(this.proxy){
11207         this.proxy = Roo.factory(this.proxy, Roo.data);
11208         this.proxy.xmodule = this.xmodule || false;
11209         this.relayEvents(this.proxy,  ["loadexception"]);
11210     }
11211     this.sortToggle = {};
11212     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11213
11214     Roo.data.Store.superclass.constructor.call(this);
11215
11216     if(this.inlineData){
11217         this.loadData(this.inlineData);
11218         delete this.inlineData;
11219     }
11220 };
11221
11222 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11223      /**
11224     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11225     * without a remote query - used by combo/forms at present.
11226     */
11227     
11228     /**
11229     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11230     */
11231     /**
11232     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11233     */
11234     /**
11235     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11236     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11237     */
11238     /**
11239     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11240     * on any HTTP request
11241     */
11242     /**
11243     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11244     */
11245     /**
11246     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11247     */
11248     multiSort: false,
11249     /**
11250     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11251     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11252     */
11253     remoteSort : false,
11254
11255     /**
11256     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11257      * loaded or when a record is removed. (defaults to false).
11258     */
11259     pruneModifiedRecords : false,
11260
11261     // private
11262     lastOptions : null,
11263
11264     /**
11265      * Add Records to the Store and fires the add event.
11266      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11267      */
11268     add : function(records){
11269         records = [].concat(records);
11270         for(var i = 0, len = records.length; i < len; i++){
11271             records[i].join(this);
11272         }
11273         var index = this.data.length;
11274         this.data.addAll(records);
11275         this.fireEvent("add", this, records, index);
11276     },
11277
11278     /**
11279      * Remove a Record from the Store and fires the remove event.
11280      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11281      */
11282     remove : function(record){
11283         var index = this.data.indexOf(record);
11284         this.data.removeAt(index);
11285  
11286         if(this.pruneModifiedRecords){
11287             this.modified.remove(record);
11288         }
11289         this.fireEvent("remove", this, record, index);
11290     },
11291
11292     /**
11293      * Remove all Records from the Store and fires the clear event.
11294      */
11295     removeAll : function(){
11296         this.data.clear();
11297         if(this.pruneModifiedRecords){
11298             this.modified = [];
11299         }
11300         this.fireEvent("clear", this);
11301     },
11302
11303     /**
11304      * Inserts Records to the Store at the given index and fires the add event.
11305      * @param {Number} index The start index at which to insert the passed Records.
11306      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11307      */
11308     insert : function(index, records){
11309         records = [].concat(records);
11310         for(var i = 0, len = records.length; i < len; i++){
11311             this.data.insert(index, records[i]);
11312             records[i].join(this);
11313         }
11314         this.fireEvent("add", this, records, index);
11315     },
11316
11317     /**
11318      * Get the index within the cache of the passed Record.
11319      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11320      * @return {Number} The index of the passed Record. Returns -1 if not found.
11321      */
11322     indexOf : function(record){
11323         return this.data.indexOf(record);
11324     },
11325
11326     /**
11327      * Get the index within the cache of the Record with the passed id.
11328      * @param {String} id The id of the Record to find.
11329      * @return {Number} The index of the Record. Returns -1 if not found.
11330      */
11331     indexOfId : function(id){
11332         return this.data.indexOfKey(id);
11333     },
11334
11335     /**
11336      * Get the Record with the specified id.
11337      * @param {String} id The id of the Record to find.
11338      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11339      */
11340     getById : function(id){
11341         return this.data.key(id);
11342     },
11343
11344     /**
11345      * Get the Record at the specified index.
11346      * @param {Number} index The index of the Record to find.
11347      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11348      */
11349     getAt : function(index){
11350         return this.data.itemAt(index);
11351     },
11352
11353     /**
11354      * Returns a range of Records between specified indices.
11355      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11356      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11357      * @return {Roo.data.Record[]} An array of Records
11358      */
11359     getRange : function(start, end){
11360         return this.data.getRange(start, end);
11361     },
11362
11363     // private
11364     storeOptions : function(o){
11365         o = Roo.apply({}, o);
11366         delete o.callback;
11367         delete o.scope;
11368         this.lastOptions = o;
11369     },
11370
11371     /**
11372      * Loads the Record cache from the configured Proxy using the configured Reader.
11373      * <p>
11374      * If using remote paging, then the first load call must specify the <em>start</em>
11375      * and <em>limit</em> properties in the options.params property to establish the initial
11376      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11377      * <p>
11378      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11379      * and this call will return before the new data has been loaded. Perform any post-processing
11380      * in a callback function, or in a "load" event handler.</strong>
11381      * <p>
11382      * @param {Object} options An object containing properties which control loading options:<ul>
11383      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11384      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11385      * passed the following arguments:<ul>
11386      * <li>r : Roo.data.Record[]</li>
11387      * <li>options: Options object from the load call</li>
11388      * <li>success: Boolean success indicator</li></ul></li>
11389      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11390      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11391      * </ul>
11392      */
11393     load : function(options){
11394         options = options || {};
11395         if(this.fireEvent("beforeload", this, options) !== false){
11396             this.storeOptions(options);
11397             var p = Roo.apply(options.params || {}, this.baseParams);
11398             // if meta was not loaded from remote source.. try requesting it.
11399             if (!this.reader.metaFromRemote) {
11400                 p._requestMeta = 1;
11401             }
11402             if(this.sortInfo && this.remoteSort){
11403                 var pn = this.paramNames;
11404                 p[pn["sort"]] = this.sortInfo.field;
11405                 p[pn["dir"]] = this.sortInfo.direction;
11406             }
11407             if (this.multiSort) {
11408                 var pn = this.paramNames;
11409                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11410             }
11411             
11412             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11413         }
11414     },
11415
11416     /**
11417      * Reloads the Record cache from the configured Proxy using the configured Reader and
11418      * the options from the last load operation performed.
11419      * @param {Object} options (optional) An object containing properties which may override the options
11420      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11421      * the most recently used options are reused).
11422      */
11423     reload : function(options){
11424         this.load(Roo.applyIf(options||{}, this.lastOptions));
11425     },
11426
11427     // private
11428     // Called as a callback by the Reader during a load operation.
11429     loadRecords : function(o, options, success){
11430         if(!o || success === false){
11431             if(success !== false){
11432                 this.fireEvent("load", this, [], options, o);
11433             }
11434             if(options.callback){
11435                 options.callback.call(options.scope || this, [], options, false);
11436             }
11437             return;
11438         }
11439         // if data returned failure - throw an exception.
11440         if (o.success === false) {
11441             // show a message if no listener is registered.
11442             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11443                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11444             }
11445             // loadmask wil be hooked into this..
11446             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11447             return;
11448         }
11449         var r = o.records, t = o.totalRecords || r.length;
11450         
11451         this.fireEvent("beforeloadadd", this, r, options, o);
11452         
11453         if(!options || options.add !== true){
11454             if(this.pruneModifiedRecords){
11455                 this.modified = [];
11456             }
11457             for(var i = 0, len = r.length; i < len; i++){
11458                 r[i].join(this);
11459             }
11460             if(this.snapshot){
11461                 this.data = this.snapshot;
11462                 delete this.snapshot;
11463             }
11464             this.data.clear();
11465             this.data.addAll(r);
11466             this.totalLength = t;
11467             this.applySort();
11468             this.fireEvent("datachanged", this);
11469         }else{
11470             this.totalLength = Math.max(t, this.data.length+r.length);
11471             this.add(r);
11472         }
11473         
11474         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11475                 
11476             var e = new Roo.data.Record({});
11477
11478             e.set(this.parent.displayField, this.parent.emptyTitle);
11479             e.set(this.parent.valueField, '');
11480
11481             this.insert(0, e);
11482         }
11483             
11484         this.fireEvent("load", this, r, options, o);
11485         if(options.callback){
11486             options.callback.call(options.scope || this, r, options, true);
11487         }
11488     },
11489
11490
11491     /**
11492      * Loads data from a passed data block. A Reader which understands the format of the data
11493      * must have been configured in the constructor.
11494      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11495      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11496      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11497      */
11498     loadData : function(o, append){
11499         var r = this.reader.readRecords(o);
11500         this.loadRecords(r, {add: append}, true);
11501     },
11502
11503     /**
11504      * Gets the number of cached records.
11505      * <p>
11506      * <em>If using paging, this may not be the total size of the dataset. If the data object
11507      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11508      * the data set size</em>
11509      */
11510     getCount : function(){
11511         return this.data.length || 0;
11512     },
11513
11514     /**
11515      * Gets the total number of records in the dataset as returned by the server.
11516      * <p>
11517      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11518      * the dataset size</em>
11519      */
11520     getTotalCount : function(){
11521         return this.totalLength || 0;
11522     },
11523
11524     /**
11525      * Returns the sort state of the Store as an object with two properties:
11526      * <pre><code>
11527  field {String} The name of the field by which the Records are sorted
11528  direction {String} The sort order, "ASC" or "DESC"
11529      * </code></pre>
11530      */
11531     getSortState : function(){
11532         return this.sortInfo;
11533     },
11534
11535     // private
11536     applySort : function(){
11537         if(this.sortInfo && !this.remoteSort){
11538             var s = this.sortInfo, f = s.field;
11539             var st = this.fields.get(f).sortType;
11540             var fn = function(r1, r2){
11541                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11542                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11543             };
11544             this.data.sort(s.direction, fn);
11545             if(this.snapshot && this.snapshot != this.data){
11546                 this.snapshot.sort(s.direction, fn);
11547             }
11548         }
11549     },
11550
11551     /**
11552      * Sets the default sort column and order to be used by the next load operation.
11553      * @param {String} fieldName The name of the field to sort by.
11554      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11555      */
11556     setDefaultSort : function(field, dir){
11557         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11558     },
11559
11560     /**
11561      * Sort the Records.
11562      * If remote sorting is used, the sort is performed on the server, and the cache is
11563      * reloaded. If local sorting is used, the cache is sorted internally.
11564      * @param {String} fieldName The name of the field to sort by.
11565      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11566      */
11567     sort : function(fieldName, dir){
11568         var f = this.fields.get(fieldName);
11569         if(!dir){
11570             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11571             
11572             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11573                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11574             }else{
11575                 dir = f.sortDir;
11576             }
11577         }
11578         this.sortToggle[f.name] = dir;
11579         this.sortInfo = {field: f.name, direction: dir};
11580         if(!this.remoteSort){
11581             this.applySort();
11582             this.fireEvent("datachanged", this);
11583         }else{
11584             this.load(this.lastOptions);
11585         }
11586     },
11587
11588     /**
11589      * Calls the specified function for each of the Records in the cache.
11590      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11591      * Returning <em>false</em> aborts and exits the iteration.
11592      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11593      */
11594     each : function(fn, scope){
11595         this.data.each(fn, scope);
11596     },
11597
11598     /**
11599      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11600      * (e.g., during paging).
11601      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11602      */
11603     getModifiedRecords : function(){
11604         return this.modified;
11605     },
11606
11607     // private
11608     createFilterFn : function(property, value, anyMatch){
11609         if(!value.exec){ // not a regex
11610             value = String(value);
11611             if(value.length == 0){
11612                 return false;
11613             }
11614             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11615         }
11616         return function(r){
11617             return value.test(r.data[property]);
11618         };
11619     },
11620
11621     /**
11622      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11623      * @param {String} property A field on your records
11624      * @param {Number} start The record index to start at (defaults to 0)
11625      * @param {Number} end The last record index to include (defaults to length - 1)
11626      * @return {Number} The sum
11627      */
11628     sum : function(property, start, end){
11629         var rs = this.data.items, v = 0;
11630         start = start || 0;
11631         end = (end || end === 0) ? end : rs.length-1;
11632
11633         for(var i = start; i <= end; i++){
11634             v += (rs[i].data[property] || 0);
11635         }
11636         return v;
11637     },
11638
11639     /**
11640      * Filter the records by a specified property.
11641      * @param {String} field A field on your records
11642      * @param {String/RegExp} value Either a string that the field
11643      * should start with or a RegExp to test against the field
11644      * @param {Boolean} anyMatch True to match any part not just the beginning
11645      */
11646     filter : function(property, value, anyMatch){
11647         var fn = this.createFilterFn(property, value, anyMatch);
11648         return fn ? this.filterBy(fn) : this.clearFilter();
11649     },
11650
11651     /**
11652      * Filter by a function. The specified function will be called with each
11653      * record in this data source. If the function returns true the record is included,
11654      * otherwise it is filtered.
11655      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11656      * @param {Object} scope (optional) The scope of the function (defaults to this)
11657      */
11658     filterBy : function(fn, scope){
11659         this.snapshot = this.snapshot || this.data;
11660         this.data = this.queryBy(fn, scope||this);
11661         this.fireEvent("datachanged", this);
11662     },
11663
11664     /**
11665      * Query the records by a specified property.
11666      * @param {String} field A field on your records
11667      * @param {String/RegExp} value Either a string that the field
11668      * should start with or a RegExp to test against the field
11669      * @param {Boolean} anyMatch True to match any part not just the beginning
11670      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11671      */
11672     query : function(property, value, anyMatch){
11673         var fn = this.createFilterFn(property, value, anyMatch);
11674         return fn ? this.queryBy(fn) : this.data.clone();
11675     },
11676
11677     /**
11678      * Query by a function. The specified function will be called with each
11679      * record in this data source. If the function returns true the record is included
11680      * in the results.
11681      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11682      * @param {Object} scope (optional) The scope of the function (defaults to this)
11683       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11684      **/
11685     queryBy : function(fn, scope){
11686         var data = this.snapshot || this.data;
11687         return data.filterBy(fn, scope||this);
11688     },
11689
11690     /**
11691      * Collects unique values for a particular dataIndex from this store.
11692      * @param {String} dataIndex The property to collect
11693      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11694      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11695      * @return {Array} An array of the unique values
11696      **/
11697     collect : function(dataIndex, allowNull, bypassFilter){
11698         var d = (bypassFilter === true && this.snapshot) ?
11699                 this.snapshot.items : this.data.items;
11700         var v, sv, r = [], l = {};
11701         for(var i = 0, len = d.length; i < len; i++){
11702             v = d[i].data[dataIndex];
11703             sv = String(v);
11704             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11705                 l[sv] = true;
11706                 r[r.length] = v;
11707             }
11708         }
11709         return r;
11710     },
11711
11712     /**
11713      * Revert to a view of the Record cache with no filtering applied.
11714      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11715      */
11716     clearFilter : function(suppressEvent){
11717         if(this.snapshot && this.snapshot != this.data){
11718             this.data = this.snapshot;
11719             delete this.snapshot;
11720             if(suppressEvent !== true){
11721                 this.fireEvent("datachanged", this);
11722             }
11723         }
11724     },
11725
11726     // private
11727     afterEdit : function(record){
11728         if(this.modified.indexOf(record) == -1){
11729             this.modified.push(record);
11730         }
11731         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11732     },
11733     
11734     // private
11735     afterReject : function(record){
11736         this.modified.remove(record);
11737         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11738     },
11739
11740     // private
11741     afterCommit : function(record){
11742         this.modified.remove(record);
11743         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11744     },
11745
11746     /**
11747      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11748      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11749      */
11750     commitChanges : function(){
11751         var m = this.modified.slice(0);
11752         this.modified = [];
11753         for(var i = 0, len = m.length; i < len; i++){
11754             m[i].commit();
11755         }
11756     },
11757
11758     /**
11759      * Cancel outstanding changes on all changed records.
11760      */
11761     rejectChanges : function(){
11762         var m = this.modified.slice(0);
11763         this.modified = [];
11764         for(var i = 0, len = m.length; i < len; i++){
11765             m[i].reject();
11766         }
11767     },
11768
11769     onMetaChange : function(meta, rtype, o){
11770         this.recordType = rtype;
11771         this.fields = rtype.prototype.fields;
11772         delete this.snapshot;
11773         this.sortInfo = meta.sortInfo || this.sortInfo;
11774         this.modified = [];
11775         this.fireEvent('metachange', this, this.reader.meta);
11776     },
11777     
11778     moveIndex : function(data, type)
11779     {
11780         var index = this.indexOf(data);
11781         
11782         var newIndex = index + type;
11783         
11784         this.remove(data);
11785         
11786         this.insert(newIndex, data);
11787         
11788     }
11789 });/*
11790  * Based on:
11791  * Ext JS Library 1.1.1
11792  * Copyright(c) 2006-2007, Ext JS, LLC.
11793  *
11794  * Originally Released Under LGPL - original licence link has changed is not relivant.
11795  *
11796  * Fork - LGPL
11797  * <script type="text/javascript">
11798  */
11799
11800 /**
11801  * @class Roo.data.SimpleStore
11802  * @extends Roo.data.Store
11803  * Small helper class to make creating Stores from Array data easier.
11804  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11805  * @cfg {Array} fields An array of field definition objects, or field name strings.
11806  * @cfg {Array} data The multi-dimensional array of data
11807  * @constructor
11808  * @param {Object} config
11809  */
11810 Roo.data.SimpleStore = function(config){
11811     Roo.data.SimpleStore.superclass.constructor.call(this, {
11812         isLocal : true,
11813         reader: new Roo.data.ArrayReader({
11814                 id: config.id
11815             },
11816             Roo.data.Record.create(config.fields)
11817         ),
11818         proxy : new Roo.data.MemoryProxy(config.data)
11819     });
11820     this.load();
11821 };
11822 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11823  * Based on:
11824  * Ext JS Library 1.1.1
11825  * Copyright(c) 2006-2007, Ext JS, LLC.
11826  *
11827  * Originally Released Under LGPL - original licence link has changed is not relivant.
11828  *
11829  * Fork - LGPL
11830  * <script type="text/javascript">
11831  */
11832
11833 /**
11834 /**
11835  * @extends Roo.data.Store
11836  * @class Roo.data.JsonStore
11837  * Small helper class to make creating Stores for JSON data easier. <br/>
11838 <pre><code>
11839 var store = new Roo.data.JsonStore({
11840     url: 'get-images.php',
11841     root: 'images',
11842     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11843 });
11844 </code></pre>
11845  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11846  * JsonReader and HttpProxy (unless inline data is provided).</b>
11847  * @cfg {Array} fields An array of field definition objects, or field name strings.
11848  * @constructor
11849  * @param {Object} config
11850  */
11851 Roo.data.JsonStore = function(c){
11852     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11853         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11854         reader: new Roo.data.JsonReader(c, c.fields)
11855     }));
11856 };
11857 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11858  * Based on:
11859  * Ext JS Library 1.1.1
11860  * Copyright(c) 2006-2007, Ext JS, LLC.
11861  *
11862  * Originally Released Under LGPL - original licence link has changed is not relivant.
11863  *
11864  * Fork - LGPL
11865  * <script type="text/javascript">
11866  */
11867
11868  
11869 Roo.data.Field = function(config){
11870     if(typeof config == "string"){
11871         config = {name: config};
11872     }
11873     Roo.apply(this, config);
11874     
11875     if(!this.type){
11876         this.type = "auto";
11877     }
11878     
11879     var st = Roo.data.SortTypes;
11880     // named sortTypes are supported, here we look them up
11881     if(typeof this.sortType == "string"){
11882         this.sortType = st[this.sortType];
11883     }
11884     
11885     // set default sortType for strings and dates
11886     if(!this.sortType){
11887         switch(this.type){
11888             case "string":
11889                 this.sortType = st.asUCString;
11890                 break;
11891             case "date":
11892                 this.sortType = st.asDate;
11893                 break;
11894             default:
11895                 this.sortType = st.none;
11896         }
11897     }
11898
11899     // define once
11900     var stripRe = /[\$,%]/g;
11901
11902     // prebuilt conversion function for this field, instead of
11903     // switching every time we're reading a value
11904     if(!this.convert){
11905         var cv, dateFormat = this.dateFormat;
11906         switch(this.type){
11907             case "":
11908             case "auto":
11909             case undefined:
11910                 cv = function(v){ return v; };
11911                 break;
11912             case "string":
11913                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11914                 break;
11915             case "int":
11916                 cv = function(v){
11917                     return v !== undefined && v !== null && v !== '' ?
11918                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11919                     };
11920                 break;
11921             case "float":
11922                 cv = function(v){
11923                     return v !== undefined && v !== null && v !== '' ?
11924                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11925                     };
11926                 break;
11927             case "bool":
11928             case "boolean":
11929                 cv = function(v){ return v === true || v === "true" || v == 1; };
11930                 break;
11931             case "date":
11932                 cv = function(v){
11933                     if(!v){
11934                         return '';
11935                     }
11936                     if(v instanceof Date){
11937                         return v;
11938                     }
11939                     if(dateFormat){
11940                         if(dateFormat == "timestamp"){
11941                             return new Date(v*1000);
11942                         }
11943                         return Date.parseDate(v, dateFormat);
11944                     }
11945                     var parsed = Date.parse(v);
11946                     return parsed ? new Date(parsed) : null;
11947                 };
11948              break;
11949             
11950         }
11951         this.convert = cv;
11952     }
11953 };
11954
11955 Roo.data.Field.prototype = {
11956     dateFormat: null,
11957     defaultValue: "",
11958     mapping: null,
11959     sortType : null,
11960     sortDir : "ASC"
11961 };/*
11962  * Based on:
11963  * Ext JS Library 1.1.1
11964  * Copyright(c) 2006-2007, Ext JS, LLC.
11965  *
11966  * Originally Released Under LGPL - original licence link has changed is not relivant.
11967  *
11968  * Fork - LGPL
11969  * <script type="text/javascript">
11970  */
11971  
11972 // Base class for reading structured data from a data source.  This class is intended to be
11973 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11974
11975 /**
11976  * @class Roo.data.DataReader
11977  * Base class for reading structured data from a data source.  This class is intended to be
11978  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11979  */
11980
11981 Roo.data.DataReader = function(meta, recordType){
11982     
11983     this.meta = meta;
11984     
11985     this.recordType = recordType instanceof Array ? 
11986         Roo.data.Record.create(recordType) : recordType;
11987 };
11988
11989 Roo.data.DataReader.prototype = {
11990      /**
11991      * Create an empty record
11992      * @param {Object} data (optional) - overlay some values
11993      * @return {Roo.data.Record} record created.
11994      */
11995     newRow :  function(d) {
11996         var da =  {};
11997         this.recordType.prototype.fields.each(function(c) {
11998             switch( c.type) {
11999                 case 'int' : da[c.name] = 0; break;
12000                 case 'date' : da[c.name] = new Date(); break;
12001                 case 'float' : da[c.name] = 0.0; break;
12002                 case 'boolean' : da[c.name] = false; break;
12003                 default : da[c.name] = ""; break;
12004             }
12005             
12006         });
12007         return new this.recordType(Roo.apply(da, d));
12008     }
12009     
12010 };/*
12011  * Based on:
12012  * Ext JS Library 1.1.1
12013  * Copyright(c) 2006-2007, Ext JS, LLC.
12014  *
12015  * Originally Released Under LGPL - original licence link has changed is not relivant.
12016  *
12017  * Fork - LGPL
12018  * <script type="text/javascript">
12019  */
12020
12021 /**
12022  * @class Roo.data.DataProxy
12023  * @extends Roo.data.Observable
12024  * This class is an abstract base class for implementations which provide retrieval of
12025  * unformatted data objects.<br>
12026  * <p>
12027  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12028  * (of the appropriate type which knows how to parse the data object) to provide a block of
12029  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12030  * <p>
12031  * Custom implementations must implement the load method as described in
12032  * {@link Roo.data.HttpProxy#load}.
12033  */
12034 Roo.data.DataProxy = function(){
12035     this.addEvents({
12036         /**
12037          * @event beforeload
12038          * Fires before a network request is made to retrieve a data object.
12039          * @param {Object} This DataProxy object.
12040          * @param {Object} params The params parameter to the load function.
12041          */
12042         beforeload : true,
12043         /**
12044          * @event load
12045          * Fires before the load method's callback is called.
12046          * @param {Object} This DataProxy object.
12047          * @param {Object} o The data object.
12048          * @param {Object} arg The callback argument object passed to the load function.
12049          */
12050         load : true,
12051         /**
12052          * @event loadexception
12053          * Fires if an Exception occurs during data retrieval.
12054          * @param {Object} This DataProxy object.
12055          * @param {Object} o The data object.
12056          * @param {Object} arg The callback argument object passed to the load function.
12057          * @param {Object} e The Exception.
12058          */
12059         loadexception : true
12060     });
12061     Roo.data.DataProxy.superclass.constructor.call(this);
12062 };
12063
12064 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12065
12066     /**
12067      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12068      */
12069 /*
12070  * Based on:
12071  * Ext JS Library 1.1.1
12072  * Copyright(c) 2006-2007, Ext JS, LLC.
12073  *
12074  * Originally Released Under LGPL - original licence link has changed is not relivant.
12075  *
12076  * Fork - LGPL
12077  * <script type="text/javascript">
12078  */
12079 /**
12080  * @class Roo.data.MemoryProxy
12081  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12082  * to the Reader when its load method is called.
12083  * @constructor
12084  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12085  */
12086 Roo.data.MemoryProxy = function(data){
12087     if (data.data) {
12088         data = data.data;
12089     }
12090     Roo.data.MemoryProxy.superclass.constructor.call(this);
12091     this.data = data;
12092 };
12093
12094 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12095     
12096     /**
12097      * Load data from the requested source (in this case an in-memory
12098      * data object passed to the constructor), read the data object into
12099      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12100      * process that block using the passed callback.
12101      * @param {Object} params This parameter is not used by the MemoryProxy class.
12102      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12103      * object into a block of Roo.data.Records.
12104      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12105      * The function must be passed <ul>
12106      * <li>The Record block object</li>
12107      * <li>The "arg" argument from the load function</li>
12108      * <li>A boolean success indicator</li>
12109      * </ul>
12110      * @param {Object} scope The scope in which to call the callback
12111      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12112      */
12113     load : function(params, reader, callback, scope, arg){
12114         params = params || {};
12115         var result;
12116         try {
12117             result = reader.readRecords(this.data);
12118         }catch(e){
12119             this.fireEvent("loadexception", this, arg, null, e);
12120             callback.call(scope, null, arg, false);
12121             return;
12122         }
12123         callback.call(scope, result, arg, true);
12124     },
12125     
12126     // private
12127     update : function(params, records){
12128         
12129     }
12130 });/*
12131  * Based on:
12132  * Ext JS Library 1.1.1
12133  * Copyright(c) 2006-2007, Ext JS, LLC.
12134  *
12135  * Originally Released Under LGPL - original licence link has changed is not relivant.
12136  *
12137  * Fork - LGPL
12138  * <script type="text/javascript">
12139  */
12140 /**
12141  * @class Roo.data.HttpProxy
12142  * @extends Roo.data.DataProxy
12143  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12144  * configured to reference a certain URL.<br><br>
12145  * <p>
12146  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12147  * from which the running page was served.<br><br>
12148  * <p>
12149  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12150  * <p>
12151  * Be aware that to enable the browser to parse an XML document, the server must set
12152  * the Content-Type header in the HTTP response to "text/xml".
12153  * @constructor
12154  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12155  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12156  * will be used to make the request.
12157  */
12158 Roo.data.HttpProxy = function(conn){
12159     Roo.data.HttpProxy.superclass.constructor.call(this);
12160     // is conn a conn config or a real conn?
12161     this.conn = conn;
12162     this.useAjax = !conn || !conn.events;
12163   
12164 };
12165
12166 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12167     // thse are take from connection...
12168     
12169     /**
12170      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12171      */
12172     /**
12173      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12174      * extra parameters to each request made by this object. (defaults to undefined)
12175      */
12176     /**
12177      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12178      *  to each request made by this object. (defaults to undefined)
12179      */
12180     /**
12181      * @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)
12182      */
12183     /**
12184      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12185      */
12186      /**
12187      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12188      * @type Boolean
12189      */
12190   
12191
12192     /**
12193      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12194      * @type Boolean
12195      */
12196     /**
12197      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12198      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12199      * a finer-grained basis than the DataProxy events.
12200      */
12201     getConnection : function(){
12202         return this.useAjax ? Roo.Ajax : this.conn;
12203     },
12204
12205     /**
12206      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12207      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12208      * process that block using the passed callback.
12209      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12210      * for the request to the remote server.
12211      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12212      * object into a block of Roo.data.Records.
12213      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12214      * The function must be passed <ul>
12215      * <li>The Record block object</li>
12216      * <li>The "arg" argument from the load function</li>
12217      * <li>A boolean success indicator</li>
12218      * </ul>
12219      * @param {Object} scope The scope in which to call the callback
12220      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12221      */
12222     load : function(params, reader, callback, scope, arg){
12223         if(this.fireEvent("beforeload", this, params) !== false){
12224             var  o = {
12225                 params : params || {},
12226                 request: {
12227                     callback : callback,
12228                     scope : scope,
12229                     arg : arg
12230                 },
12231                 reader: reader,
12232                 callback : this.loadResponse,
12233                 scope: this
12234             };
12235             if(this.useAjax){
12236                 Roo.applyIf(o, this.conn);
12237                 if(this.activeRequest){
12238                     Roo.Ajax.abort(this.activeRequest);
12239                 }
12240                 this.activeRequest = Roo.Ajax.request(o);
12241             }else{
12242                 this.conn.request(o);
12243             }
12244         }else{
12245             callback.call(scope||this, null, arg, false);
12246         }
12247     },
12248
12249     // private
12250     loadResponse : function(o, success, response){
12251         delete this.activeRequest;
12252         if(!success){
12253             this.fireEvent("loadexception", this, o, response);
12254             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12255             return;
12256         }
12257         var result;
12258         try {
12259             result = o.reader.read(response);
12260         }catch(e){
12261             this.fireEvent("loadexception", this, o, response, e);
12262             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12263             return;
12264         }
12265         
12266         this.fireEvent("load", this, o, o.request.arg);
12267         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12268     },
12269
12270     // private
12271     update : function(dataSet){
12272
12273     },
12274
12275     // private
12276     updateResponse : function(dataSet){
12277
12278     }
12279 });/*
12280  * Based on:
12281  * Ext JS Library 1.1.1
12282  * Copyright(c) 2006-2007, Ext JS, LLC.
12283  *
12284  * Originally Released Under LGPL - original licence link has changed is not relivant.
12285  *
12286  * Fork - LGPL
12287  * <script type="text/javascript">
12288  */
12289
12290 /**
12291  * @class Roo.data.ScriptTagProxy
12292  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12293  * other than the originating domain of the running page.<br><br>
12294  * <p>
12295  * <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
12296  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12297  * <p>
12298  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12299  * source code that is used as the source inside a &lt;script> tag.<br><br>
12300  * <p>
12301  * In order for the browser to process the returned data, the server must wrap the data object
12302  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12303  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12304  * depending on whether the callback name was passed:
12305  * <p>
12306  * <pre><code>
12307 boolean scriptTag = false;
12308 String cb = request.getParameter("callback");
12309 if (cb != null) {
12310     scriptTag = true;
12311     response.setContentType("text/javascript");
12312 } else {
12313     response.setContentType("application/x-json");
12314 }
12315 Writer out = response.getWriter();
12316 if (scriptTag) {
12317     out.write(cb + "(");
12318 }
12319 out.print(dataBlock.toJsonString());
12320 if (scriptTag) {
12321     out.write(");");
12322 }
12323 </pre></code>
12324  *
12325  * @constructor
12326  * @param {Object} config A configuration object.
12327  */
12328 Roo.data.ScriptTagProxy = function(config){
12329     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12330     Roo.apply(this, config);
12331     this.head = document.getElementsByTagName("head")[0];
12332 };
12333
12334 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12335
12336 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12337     /**
12338      * @cfg {String} url The URL from which to request the data object.
12339      */
12340     /**
12341      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12342      */
12343     timeout : 30000,
12344     /**
12345      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12346      * the server the name of the callback function set up by the load call to process the returned data object.
12347      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12348      * javascript output which calls this named function passing the data object as its only parameter.
12349      */
12350     callbackParam : "callback",
12351     /**
12352      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12353      * name to the request.
12354      */
12355     nocache : true,
12356
12357     /**
12358      * Load data from the configured URL, read the data object into
12359      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12360      * process that block using the passed callback.
12361      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12362      * for the request to the remote server.
12363      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12364      * object into a block of Roo.data.Records.
12365      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12366      * The function must be passed <ul>
12367      * <li>The Record block object</li>
12368      * <li>The "arg" argument from the load function</li>
12369      * <li>A boolean success indicator</li>
12370      * </ul>
12371      * @param {Object} scope The scope in which to call the callback
12372      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12373      */
12374     load : function(params, reader, callback, scope, arg){
12375         if(this.fireEvent("beforeload", this, params) !== false){
12376
12377             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12378
12379             var url = this.url;
12380             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12381             if(this.nocache){
12382                 url += "&_dc=" + (new Date().getTime());
12383             }
12384             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12385             var trans = {
12386                 id : transId,
12387                 cb : "stcCallback"+transId,
12388                 scriptId : "stcScript"+transId,
12389                 params : params,
12390                 arg : arg,
12391                 url : url,
12392                 callback : callback,
12393                 scope : scope,
12394                 reader : reader
12395             };
12396             var conn = this;
12397
12398             window[trans.cb] = function(o){
12399                 conn.handleResponse(o, trans);
12400             };
12401
12402             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12403
12404             if(this.autoAbort !== false){
12405                 this.abort();
12406             }
12407
12408             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12409
12410             var script = document.createElement("script");
12411             script.setAttribute("src", url);
12412             script.setAttribute("type", "text/javascript");
12413             script.setAttribute("id", trans.scriptId);
12414             this.head.appendChild(script);
12415
12416             this.trans = trans;
12417         }else{
12418             callback.call(scope||this, null, arg, false);
12419         }
12420     },
12421
12422     // private
12423     isLoading : function(){
12424         return this.trans ? true : false;
12425     },
12426
12427     /**
12428      * Abort the current server request.
12429      */
12430     abort : function(){
12431         if(this.isLoading()){
12432             this.destroyTrans(this.trans);
12433         }
12434     },
12435
12436     // private
12437     destroyTrans : function(trans, isLoaded){
12438         this.head.removeChild(document.getElementById(trans.scriptId));
12439         clearTimeout(trans.timeoutId);
12440         if(isLoaded){
12441             window[trans.cb] = undefined;
12442             try{
12443                 delete window[trans.cb];
12444             }catch(e){}
12445         }else{
12446             // if hasn't been loaded, wait for load to remove it to prevent script error
12447             window[trans.cb] = function(){
12448                 window[trans.cb] = undefined;
12449                 try{
12450                     delete window[trans.cb];
12451                 }catch(e){}
12452             };
12453         }
12454     },
12455
12456     // private
12457     handleResponse : function(o, trans){
12458         this.trans = false;
12459         this.destroyTrans(trans, true);
12460         var result;
12461         try {
12462             result = trans.reader.readRecords(o);
12463         }catch(e){
12464             this.fireEvent("loadexception", this, o, trans.arg, e);
12465             trans.callback.call(trans.scope||window, null, trans.arg, false);
12466             return;
12467         }
12468         this.fireEvent("load", this, o, trans.arg);
12469         trans.callback.call(trans.scope||window, result, trans.arg, true);
12470     },
12471
12472     // private
12473     handleFailure : function(trans){
12474         this.trans = false;
12475         this.destroyTrans(trans, false);
12476         this.fireEvent("loadexception", this, null, trans.arg);
12477         trans.callback.call(trans.scope||window, null, trans.arg, false);
12478     }
12479 });/*
12480  * Based on:
12481  * Ext JS Library 1.1.1
12482  * Copyright(c) 2006-2007, Ext JS, LLC.
12483  *
12484  * Originally Released Under LGPL - original licence link has changed is not relivant.
12485  *
12486  * Fork - LGPL
12487  * <script type="text/javascript">
12488  */
12489
12490 /**
12491  * @class Roo.data.JsonReader
12492  * @extends Roo.data.DataReader
12493  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12494  * based on mappings in a provided Roo.data.Record constructor.
12495  * 
12496  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12497  * in the reply previously. 
12498  * 
12499  * <p>
12500  * Example code:
12501  * <pre><code>
12502 var RecordDef = Roo.data.Record.create([
12503     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12504     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12505 ]);
12506 var myReader = new Roo.data.JsonReader({
12507     totalProperty: "results",    // The property which contains the total dataset size (optional)
12508     root: "rows",                // The property which contains an Array of row objects
12509     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12510 }, RecordDef);
12511 </code></pre>
12512  * <p>
12513  * This would consume a JSON file like this:
12514  * <pre><code>
12515 { 'results': 2, 'rows': [
12516     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12517     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12518 }
12519 </code></pre>
12520  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12521  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12522  * paged from the remote server.
12523  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12524  * @cfg {String} root name of the property which contains the Array of row objects.
12525  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12526  * @cfg {Array} fields Array of field definition objects
12527  * @constructor
12528  * Create a new JsonReader
12529  * @param {Object} meta Metadata configuration options
12530  * @param {Object} recordType Either an Array of field definition objects,
12531  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12532  */
12533 Roo.data.JsonReader = function(meta, recordType){
12534     
12535     meta = meta || {};
12536     // set some defaults:
12537     Roo.applyIf(meta, {
12538         totalProperty: 'total',
12539         successProperty : 'success',
12540         root : 'data',
12541         id : 'id'
12542     });
12543     
12544     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12545 };
12546 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12547     
12548     /**
12549      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12550      * Used by Store query builder to append _requestMeta to params.
12551      * 
12552      */
12553     metaFromRemote : false,
12554     /**
12555      * This method is only used by a DataProxy which has retrieved data from a remote server.
12556      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12557      * @return {Object} data A data block which is used by an Roo.data.Store object as
12558      * a cache of Roo.data.Records.
12559      */
12560     read : function(response){
12561         var json = response.responseText;
12562        
12563         var o = /* eval:var:o */ eval("("+json+")");
12564         if(!o) {
12565             throw {message: "JsonReader.read: Json object not found"};
12566         }
12567         
12568         if(o.metaData){
12569             
12570             delete this.ef;
12571             this.metaFromRemote = true;
12572             this.meta = o.metaData;
12573             this.recordType = Roo.data.Record.create(o.metaData.fields);
12574             this.onMetaChange(this.meta, this.recordType, o);
12575         }
12576         return this.readRecords(o);
12577     },
12578
12579     // private function a store will implement
12580     onMetaChange : function(meta, recordType, o){
12581
12582     },
12583
12584     /**
12585          * @ignore
12586          */
12587     simpleAccess: function(obj, subsc) {
12588         return obj[subsc];
12589     },
12590
12591         /**
12592          * @ignore
12593          */
12594     getJsonAccessor: function(){
12595         var re = /[\[\.]/;
12596         return function(expr) {
12597             try {
12598                 return(re.test(expr))
12599                     ? new Function("obj", "return obj." + expr)
12600                     : function(obj){
12601                         return obj[expr];
12602                     };
12603             } catch(e){}
12604             return Roo.emptyFn;
12605         };
12606     }(),
12607
12608     /**
12609      * Create a data block containing Roo.data.Records from an XML document.
12610      * @param {Object} o An object which contains an Array of row objects in the property specified
12611      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12612      * which contains the total size of the dataset.
12613      * @return {Object} data A data block which is used by an Roo.data.Store object as
12614      * a cache of Roo.data.Records.
12615      */
12616     readRecords : function(o){
12617         /**
12618          * After any data loads, the raw JSON data is available for further custom processing.
12619          * @type Object
12620          */
12621         this.o = o;
12622         var s = this.meta, Record = this.recordType,
12623             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12624
12625 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12626         if (!this.ef) {
12627             if(s.totalProperty) {
12628                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12629                 }
12630                 if(s.successProperty) {
12631                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12632                 }
12633                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12634                 if (s.id) {
12635                         var g = this.getJsonAccessor(s.id);
12636                         this.getId = function(rec) {
12637                                 var r = g(rec);  
12638                                 return (r === undefined || r === "") ? null : r;
12639                         };
12640                 } else {
12641                         this.getId = function(){return null;};
12642                 }
12643             this.ef = [];
12644             for(var jj = 0; jj < fl; jj++){
12645                 f = fi[jj];
12646                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12647                 this.ef[jj] = this.getJsonAccessor(map);
12648             }
12649         }
12650
12651         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12652         if(s.totalProperty){
12653             var vt = parseInt(this.getTotal(o), 10);
12654             if(!isNaN(vt)){
12655                 totalRecords = vt;
12656             }
12657         }
12658         if(s.successProperty){
12659             var vs = this.getSuccess(o);
12660             if(vs === false || vs === 'false'){
12661                 success = false;
12662             }
12663         }
12664         var records = [];
12665         for(var i = 0; i < c; i++){
12666                 var n = root[i];
12667             var values = {};
12668             var id = this.getId(n);
12669             for(var j = 0; j < fl; j++){
12670                 f = fi[j];
12671             var v = this.ef[j](n);
12672             if (!f.convert) {
12673                 Roo.log('missing convert for ' + f.name);
12674                 Roo.log(f);
12675                 continue;
12676             }
12677             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12678             }
12679             var record = new Record(values, id);
12680             record.json = n;
12681             records[i] = record;
12682         }
12683         return {
12684             raw : o,
12685             success : success,
12686             records : records,
12687             totalRecords : totalRecords
12688         };
12689     }
12690 });/*
12691  * Based on:
12692  * Ext JS Library 1.1.1
12693  * Copyright(c) 2006-2007, Ext JS, LLC.
12694  *
12695  * Originally Released Under LGPL - original licence link has changed is not relivant.
12696  *
12697  * Fork - LGPL
12698  * <script type="text/javascript">
12699  */
12700
12701 /**
12702  * @class Roo.data.ArrayReader
12703  * @extends Roo.data.DataReader
12704  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12705  * Each element of that Array represents a row of data fields. The
12706  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12707  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12708  * <p>
12709  * Example code:.
12710  * <pre><code>
12711 var RecordDef = Roo.data.Record.create([
12712     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12713     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12714 ]);
12715 var myReader = new Roo.data.ArrayReader({
12716     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12717 }, RecordDef);
12718 </code></pre>
12719  * <p>
12720  * This would consume an Array like this:
12721  * <pre><code>
12722 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12723   </code></pre>
12724  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12725  * @constructor
12726  * Create a new JsonReader
12727  * @param {Object} meta Metadata configuration options.
12728  * @param {Object} recordType Either an Array of field definition objects
12729  * as specified to {@link Roo.data.Record#create},
12730  * or an {@link Roo.data.Record} object
12731  * created using {@link Roo.data.Record#create}.
12732  */
12733 Roo.data.ArrayReader = function(meta, recordType){
12734     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12735 };
12736
12737 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12738     /**
12739      * Create a data block containing Roo.data.Records from an XML document.
12740      * @param {Object} o An Array of row objects which represents the dataset.
12741      * @return {Object} data A data block which is used by an Roo.data.Store object as
12742      * a cache of Roo.data.Records.
12743      */
12744     readRecords : function(o){
12745         var sid = this.meta ? this.meta.id : null;
12746         var recordType = this.recordType, fields = recordType.prototype.fields;
12747         var records = [];
12748         var root = o;
12749             for(var i = 0; i < root.length; i++){
12750                     var n = root[i];
12751                 var values = {};
12752                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12753                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12754                 var f = fields.items[j];
12755                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12756                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12757                 v = f.convert(v);
12758                 values[f.name] = v;
12759             }
12760                 var record = new recordType(values, id);
12761                 record.json = n;
12762                 records[records.length] = record;
12763             }
12764             return {
12765                 records : records,
12766                 totalRecords : records.length
12767             };
12768     }
12769 });/*
12770  * - LGPL
12771  * * 
12772  */
12773
12774 /**
12775  * @class Roo.bootstrap.ComboBox
12776  * @extends Roo.bootstrap.TriggerField
12777  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12778  * @cfg {Boolean} append (true|false) default false
12779  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12780  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12781  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12782  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12783  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12784  * @cfg {Boolean} animate default true
12785  * @cfg {Boolean} emptyResultText only for touch device
12786  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12787  * @cfg {String} emptyTitle default ''
12788  * @constructor
12789  * Create a new ComboBox.
12790  * @param {Object} config Configuration options
12791  */
12792 Roo.bootstrap.ComboBox = function(config){
12793     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12794     this.addEvents({
12795         /**
12796          * @event expand
12797          * Fires when the dropdown list is expanded
12798         * @param {Roo.bootstrap.ComboBox} combo This combo box
12799         */
12800         'expand' : true,
12801         /**
12802          * @event collapse
12803          * Fires when the dropdown list is collapsed
12804         * @param {Roo.bootstrap.ComboBox} combo This combo box
12805         */
12806         'collapse' : true,
12807         /**
12808          * @event beforeselect
12809          * Fires before a list item is selected. Return false to cancel the selection.
12810         * @param {Roo.bootstrap.ComboBox} combo This combo box
12811         * @param {Roo.data.Record} record The data record returned from the underlying store
12812         * @param {Number} index The index of the selected item in the dropdown list
12813         */
12814         'beforeselect' : true,
12815         /**
12816          * @event select
12817          * Fires when a list item is selected
12818         * @param {Roo.bootstrap.ComboBox} combo This combo box
12819         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12820         * @param {Number} index The index of the selected item in the dropdown list
12821         */
12822         'select' : true,
12823         /**
12824          * @event beforequery
12825          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12826          * The event object passed has these properties:
12827         * @param {Roo.bootstrap.ComboBox} combo This combo box
12828         * @param {String} query The query
12829         * @param {Boolean} forceAll true to force "all" query
12830         * @param {Boolean} cancel true to cancel the query
12831         * @param {Object} e The query event object
12832         */
12833         'beforequery': true,
12834          /**
12835          * @event add
12836          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12837         * @param {Roo.bootstrap.ComboBox} combo This combo box
12838         */
12839         'add' : true,
12840         /**
12841          * @event edit
12842          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12843         * @param {Roo.bootstrap.ComboBox} combo This combo box
12844         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12845         */
12846         'edit' : true,
12847         /**
12848          * @event remove
12849          * Fires when the remove value from the combobox array
12850         * @param {Roo.bootstrap.ComboBox} combo This combo box
12851         */
12852         'remove' : true,
12853         /**
12854          * @event afterremove
12855          * Fires when the remove value from the combobox array
12856         * @param {Roo.bootstrap.ComboBox} combo This combo box
12857         */
12858         'afterremove' : true,
12859         /**
12860          * @event specialfilter
12861          * Fires when specialfilter
12862             * @param {Roo.bootstrap.ComboBox} combo This combo box
12863             */
12864         'specialfilter' : true,
12865         /**
12866          * @event tick
12867          * Fires when tick the element
12868             * @param {Roo.bootstrap.ComboBox} combo This combo box
12869             */
12870         'tick' : true,
12871         /**
12872          * @event touchviewdisplay
12873          * Fires when touch view require special display (default is using displayField)
12874             * @param {Roo.bootstrap.ComboBox} combo This combo box
12875             * @param {Object} cfg set html .
12876             */
12877         'touchviewdisplay' : true
12878         
12879     });
12880     
12881     this.item = [];
12882     this.tickItems = [];
12883     
12884     this.selectedIndex = -1;
12885     if(this.mode == 'local'){
12886         if(config.queryDelay === undefined){
12887             this.queryDelay = 10;
12888         }
12889         if(config.minChars === undefined){
12890             this.minChars = 0;
12891         }
12892     }
12893 };
12894
12895 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12896      
12897     /**
12898      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12899      * rendering into an Roo.Editor, defaults to false)
12900      */
12901     /**
12902      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12903      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12904      */
12905     /**
12906      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12907      */
12908     /**
12909      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12910      * the dropdown list (defaults to undefined, with no header element)
12911      */
12912
12913      /**
12914      * @cfg {String/Roo.Template} tpl The template to use to render the output
12915      */
12916      
12917      /**
12918      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12919      */
12920     listWidth: undefined,
12921     /**
12922      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12923      * mode = 'remote' or 'text' if mode = 'local')
12924      */
12925     displayField: undefined,
12926     
12927     /**
12928      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12929      * mode = 'remote' or 'value' if mode = 'local'). 
12930      * Note: use of a valueField requires the user make a selection
12931      * in order for a value to be mapped.
12932      */
12933     valueField: undefined,
12934     /**
12935      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12936      */
12937     modalTitle : '',
12938     
12939     /**
12940      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12941      * field's data value (defaults to the underlying DOM element's name)
12942      */
12943     hiddenName: undefined,
12944     /**
12945      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12946      */
12947     listClass: '',
12948     /**
12949      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12950      */
12951     selectedClass: 'active',
12952     
12953     /**
12954      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12955      */
12956     shadow:'sides',
12957     /**
12958      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12959      * anchor positions (defaults to 'tl-bl')
12960      */
12961     listAlign: 'tl-bl?',
12962     /**
12963      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12964      */
12965     maxHeight: 300,
12966     /**
12967      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12968      * query specified by the allQuery config option (defaults to 'query')
12969      */
12970     triggerAction: 'query',
12971     /**
12972      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12973      * (defaults to 4, does not apply if editable = false)
12974      */
12975     minChars : 4,
12976     /**
12977      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12978      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12979      */
12980     typeAhead: false,
12981     /**
12982      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12983      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12984      */
12985     queryDelay: 500,
12986     /**
12987      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12988      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12989      */
12990     pageSize: 0,
12991     /**
12992      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12993      * when editable = true (defaults to false)
12994      */
12995     selectOnFocus:false,
12996     /**
12997      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12998      */
12999     queryParam: 'query',
13000     /**
13001      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13002      * when mode = 'remote' (defaults to 'Loading...')
13003      */
13004     loadingText: 'Loading...',
13005     /**
13006      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13007      */
13008     resizable: false,
13009     /**
13010      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13011      */
13012     handleHeight : 8,
13013     /**
13014      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13015      * traditional select (defaults to true)
13016      */
13017     editable: true,
13018     /**
13019      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13020      */
13021     allQuery: '',
13022     /**
13023      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13024      */
13025     mode: 'remote',
13026     /**
13027      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13028      * listWidth has a higher value)
13029      */
13030     minListWidth : 70,
13031     /**
13032      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13033      * allow the user to set arbitrary text into the field (defaults to false)
13034      */
13035     forceSelection:false,
13036     /**
13037      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13038      * if typeAhead = true (defaults to 250)
13039      */
13040     typeAheadDelay : 250,
13041     /**
13042      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13043      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13044      */
13045     valueNotFoundText : undefined,
13046     /**
13047      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13048      */
13049     blockFocus : false,
13050     
13051     /**
13052      * @cfg {Boolean} disableClear Disable showing of clear button.
13053      */
13054     disableClear : false,
13055     /**
13056      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13057      */
13058     alwaysQuery : false,
13059     
13060     /**
13061      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13062      */
13063     multiple : false,
13064     
13065     /**
13066      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13067      */
13068     invalidClass : "has-warning",
13069     
13070     /**
13071      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13072      */
13073     validClass : "has-success",
13074     
13075     /**
13076      * @cfg {Boolean} specialFilter (true|false) special filter default false
13077      */
13078     specialFilter : false,
13079     
13080     /**
13081      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13082      */
13083     mobileTouchView : true,
13084     
13085     /**
13086      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13087      */
13088     useNativeIOS : false,
13089     
13090     /**
13091      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13092      */
13093     mobile_restrict_height : false,
13094     
13095     ios_options : false,
13096     
13097     //private
13098     addicon : false,
13099     editicon: false,
13100     
13101     page: 0,
13102     hasQuery: false,
13103     append: false,
13104     loadNext: false,
13105     autoFocus : true,
13106     tickable : false,
13107     btnPosition : 'right',
13108     triggerList : true,
13109     showToggleBtn : true,
13110     animate : true,
13111     emptyResultText: 'Empty',
13112     triggerText : 'Select',
13113     emptyTitle : '',
13114     
13115     // element that contains real text value.. (when hidden is used..)
13116     
13117     getAutoCreate : function()
13118     {   
13119         var cfg = false;
13120         //render
13121         /*
13122          * Render classic select for iso
13123          */
13124         
13125         if(Roo.isIOS && this.useNativeIOS){
13126             cfg = this.getAutoCreateNativeIOS();
13127             return cfg;
13128         }
13129         
13130         /*
13131          * Touch Devices
13132          */
13133         
13134         if(Roo.isTouch && this.mobileTouchView){
13135             cfg = this.getAutoCreateTouchView();
13136             return cfg;;
13137         }
13138         
13139         /*
13140          *  Normal ComboBox
13141          */
13142         if(!this.tickable){
13143             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13144             return cfg;
13145         }
13146         
13147         /*
13148          *  ComboBox with tickable selections
13149          */
13150              
13151         var align = this.labelAlign || this.parentLabelAlign();
13152         
13153         cfg = {
13154             cls : 'form-group roo-combobox-tickable' //input-group
13155         };
13156         
13157         var btn_text_select = '';
13158         var btn_text_done = '';
13159         var btn_text_cancel = '';
13160         
13161         if (this.btn_text_show) {
13162             btn_text_select = 'Select';
13163             btn_text_done = 'Done';
13164             btn_text_cancel = 'Cancel'; 
13165         }
13166         
13167         var buttons = {
13168             tag : 'div',
13169             cls : 'tickable-buttons',
13170             cn : [
13171                 {
13172                     tag : 'button',
13173                     type : 'button',
13174                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13175                     //html : this.triggerText
13176                     html: btn_text_select
13177                 },
13178                 {
13179                     tag : 'button',
13180                     type : 'button',
13181                     name : 'ok',
13182                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13183                     //html : 'Done'
13184                     html: btn_text_done
13185                 },
13186                 {
13187                     tag : 'button',
13188                     type : 'button',
13189                     name : 'cancel',
13190                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13191                     //html : 'Cancel'
13192                     html: btn_text_cancel
13193                 }
13194             ]
13195         };
13196         
13197         if(this.editable){
13198             buttons.cn.unshift({
13199                 tag: 'input',
13200                 cls: 'roo-select2-search-field-input'
13201             });
13202         }
13203         
13204         var _this = this;
13205         
13206         Roo.each(buttons.cn, function(c){
13207             if (_this.size) {
13208                 c.cls += ' btn-' + _this.size;
13209             }
13210
13211             if (_this.disabled) {
13212                 c.disabled = true;
13213             }
13214         });
13215         
13216         var box = {
13217             tag: 'div',
13218             cn: [
13219                 {
13220                     tag: 'input',
13221                     type : 'hidden',
13222                     cls: 'form-hidden-field'
13223                 },
13224                 {
13225                     tag: 'ul',
13226                     cls: 'roo-select2-choices',
13227                     cn:[
13228                         {
13229                             tag: 'li',
13230                             cls: 'roo-select2-search-field',
13231                             cn: [
13232                                 buttons
13233                             ]
13234                         }
13235                     ]
13236                 }
13237             ]
13238         };
13239         
13240         var combobox = {
13241             cls: 'roo-select2-container input-group roo-select2-container-multi',
13242             cn: [
13243                 box
13244 //                {
13245 //                    tag: 'ul',
13246 //                    cls: 'typeahead typeahead-long dropdown-menu',
13247 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13248 //                }
13249             ]
13250         };
13251         
13252         if(this.hasFeedback && !this.allowBlank){
13253             
13254             var feedback = {
13255                 tag: 'span',
13256                 cls: 'glyphicon form-control-feedback'
13257             };
13258
13259             combobox.cn.push(feedback);
13260         }
13261         
13262         
13263         if (align ==='left' && this.fieldLabel.length) {
13264             
13265             cfg.cls += ' roo-form-group-label-left';
13266             
13267             cfg.cn = [
13268                 {
13269                     tag : 'i',
13270                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13271                     tooltip : 'This field is required'
13272                 },
13273                 {
13274                     tag: 'label',
13275                     'for' :  id,
13276                     cls : 'control-label',
13277                     html : this.fieldLabel
13278
13279                 },
13280                 {
13281                     cls : "", 
13282                     cn: [
13283                         combobox
13284                     ]
13285                 }
13286
13287             ];
13288             
13289             var labelCfg = cfg.cn[1];
13290             var contentCfg = cfg.cn[2];
13291             
13292
13293             if(this.indicatorpos == 'right'){
13294                 
13295                 cfg.cn = [
13296                     {
13297                         tag: 'label',
13298                         'for' :  id,
13299                         cls : 'control-label',
13300                         cn : [
13301                             {
13302                                 tag : 'span',
13303                                 html : this.fieldLabel
13304                             },
13305                             {
13306                                 tag : 'i',
13307                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13308                                 tooltip : 'This field is required'
13309                             }
13310                         ]
13311                     },
13312                     {
13313                         cls : "",
13314                         cn: [
13315                             combobox
13316                         ]
13317                     }
13318
13319                 ];
13320                 
13321                 
13322                 
13323                 labelCfg = cfg.cn[0];
13324                 contentCfg = cfg.cn[1];
13325             
13326             }
13327             
13328             if(this.labelWidth > 12){
13329                 labelCfg.style = "width: " + this.labelWidth + 'px';
13330             }
13331             
13332             if(this.labelWidth < 13 && this.labelmd == 0){
13333                 this.labelmd = this.labelWidth;
13334             }
13335             
13336             if(this.labellg > 0){
13337                 labelCfg.cls += ' col-lg-' + this.labellg;
13338                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13339             }
13340             
13341             if(this.labelmd > 0){
13342                 labelCfg.cls += ' col-md-' + this.labelmd;
13343                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13344             }
13345             
13346             if(this.labelsm > 0){
13347                 labelCfg.cls += ' col-sm-' + this.labelsm;
13348                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13349             }
13350             
13351             if(this.labelxs > 0){
13352                 labelCfg.cls += ' col-xs-' + this.labelxs;
13353                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13354             }
13355                 
13356                 
13357         } else if ( this.fieldLabel.length) {
13358 //                Roo.log(" label");
13359                  cfg.cn = [
13360                     {
13361                         tag : 'i',
13362                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13363                         tooltip : 'This field is required'
13364                     },
13365                     {
13366                         tag: 'label',
13367                         //cls : 'input-group-addon',
13368                         html : this.fieldLabel
13369                     },
13370                     combobox
13371                 ];
13372                 
13373                 if(this.indicatorpos == 'right'){
13374                     cfg.cn = [
13375                         {
13376                             tag: 'label',
13377                             //cls : 'input-group-addon',
13378                             html : this.fieldLabel
13379                         },
13380                         {
13381                             tag : 'i',
13382                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13383                             tooltip : 'This field is required'
13384                         },
13385                         combobox
13386                     ];
13387                     
13388                 }
13389
13390         } else {
13391             
13392 //                Roo.log(" no label && no align");
13393                 cfg = combobox
13394                      
13395                 
13396         }
13397          
13398         var settings=this;
13399         ['xs','sm','md','lg'].map(function(size){
13400             if (settings[size]) {
13401                 cfg.cls += ' col-' + size + '-' + settings[size];
13402             }
13403         });
13404         
13405         return cfg;
13406         
13407     },
13408     
13409     _initEventsCalled : false,
13410     
13411     // private
13412     initEvents: function()
13413     {   
13414         if (this._initEventsCalled) { // as we call render... prevent looping...
13415             return;
13416         }
13417         this._initEventsCalled = true;
13418         
13419         if (!this.store) {
13420             throw "can not find store for combo";
13421         }
13422         
13423         this.indicator = this.indicatorEl();
13424         
13425         this.store = Roo.factory(this.store, Roo.data);
13426         this.store.parent = this;
13427         
13428         // if we are building from html. then this element is so complex, that we can not really
13429         // use the rendered HTML.
13430         // so we have to trash and replace the previous code.
13431         if (Roo.XComponent.build_from_html) {
13432             // remove this element....
13433             var e = this.el.dom, k=0;
13434             while (e ) { e = e.previousSibling;  ++k;}
13435
13436             this.el.remove();
13437             
13438             this.el=false;
13439             this.rendered = false;
13440             
13441             this.render(this.parent().getChildContainer(true), k);
13442         }
13443         
13444         if(Roo.isIOS && this.useNativeIOS){
13445             this.initIOSView();
13446             return;
13447         }
13448         
13449         /*
13450          * Touch Devices
13451          */
13452         
13453         if(Roo.isTouch && this.mobileTouchView){
13454             this.initTouchView();
13455             return;
13456         }
13457         
13458         if(this.tickable){
13459             this.initTickableEvents();
13460             return;
13461         }
13462         
13463         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13464         
13465         if(this.hiddenName){
13466             
13467             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13468             
13469             this.hiddenField.dom.value =
13470                 this.hiddenValue !== undefined ? this.hiddenValue :
13471                 this.value !== undefined ? this.value : '';
13472
13473             // prevent input submission
13474             this.el.dom.removeAttribute('name');
13475             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13476              
13477              
13478         }
13479         //if(Roo.isGecko){
13480         //    this.el.dom.setAttribute('autocomplete', 'off');
13481         //}
13482         
13483         var cls = 'x-combo-list';
13484         
13485         //this.list = new Roo.Layer({
13486         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13487         //});
13488         
13489         var _this = this;
13490         
13491         (function(){
13492             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13493             _this.list.setWidth(lw);
13494         }).defer(100);
13495         
13496         this.list.on('mouseover', this.onViewOver, this);
13497         this.list.on('mousemove', this.onViewMove, this);
13498         this.list.on('scroll', this.onViewScroll, this);
13499         
13500         /*
13501         this.list.swallowEvent('mousewheel');
13502         this.assetHeight = 0;
13503
13504         if(this.title){
13505             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13506             this.assetHeight += this.header.getHeight();
13507         }
13508
13509         this.innerList = this.list.createChild({cls:cls+'-inner'});
13510         this.innerList.on('mouseover', this.onViewOver, this);
13511         this.innerList.on('mousemove', this.onViewMove, this);
13512         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13513         
13514         if(this.allowBlank && !this.pageSize && !this.disableClear){
13515             this.footer = this.list.createChild({cls:cls+'-ft'});
13516             this.pageTb = new Roo.Toolbar(this.footer);
13517            
13518         }
13519         if(this.pageSize){
13520             this.footer = this.list.createChild({cls:cls+'-ft'});
13521             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13522                     {pageSize: this.pageSize});
13523             
13524         }
13525         
13526         if (this.pageTb && this.allowBlank && !this.disableClear) {
13527             var _this = this;
13528             this.pageTb.add(new Roo.Toolbar.Fill(), {
13529                 cls: 'x-btn-icon x-btn-clear',
13530                 text: '&#160;',
13531                 handler: function()
13532                 {
13533                     _this.collapse();
13534                     _this.clearValue();
13535                     _this.onSelect(false, -1);
13536                 }
13537             });
13538         }
13539         if (this.footer) {
13540             this.assetHeight += this.footer.getHeight();
13541         }
13542         */
13543             
13544         if(!this.tpl){
13545             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13546         }
13547
13548         this.view = new Roo.View(this.list, this.tpl, {
13549             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13550         });
13551         //this.view.wrapEl.setDisplayed(false);
13552         this.view.on('click', this.onViewClick, this);
13553         
13554         
13555         this.store.on('beforeload', this.onBeforeLoad, this);
13556         this.store.on('load', this.onLoad, this);
13557         this.store.on('loadexception', this.onLoadException, this);
13558         /*
13559         if(this.resizable){
13560             this.resizer = new Roo.Resizable(this.list,  {
13561                pinned:true, handles:'se'
13562             });
13563             this.resizer.on('resize', function(r, w, h){
13564                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13565                 this.listWidth = w;
13566                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13567                 this.restrictHeight();
13568             }, this);
13569             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13570         }
13571         */
13572         if(!this.editable){
13573             this.editable = true;
13574             this.setEditable(false);
13575         }
13576         
13577         /*
13578         
13579         if (typeof(this.events.add.listeners) != 'undefined') {
13580             
13581             this.addicon = this.wrap.createChild(
13582                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13583        
13584             this.addicon.on('click', function(e) {
13585                 this.fireEvent('add', this);
13586             }, this);
13587         }
13588         if (typeof(this.events.edit.listeners) != 'undefined') {
13589             
13590             this.editicon = this.wrap.createChild(
13591                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13592             if (this.addicon) {
13593                 this.editicon.setStyle('margin-left', '40px');
13594             }
13595             this.editicon.on('click', function(e) {
13596                 
13597                 // we fire even  if inothing is selected..
13598                 this.fireEvent('edit', this, this.lastData );
13599                 
13600             }, this);
13601         }
13602         */
13603         
13604         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13605             "up" : function(e){
13606                 this.inKeyMode = true;
13607                 this.selectPrev();
13608             },
13609
13610             "down" : function(e){
13611                 if(!this.isExpanded()){
13612                     this.onTriggerClick();
13613                 }else{
13614                     this.inKeyMode = true;
13615                     this.selectNext();
13616                 }
13617             },
13618
13619             "enter" : function(e){
13620 //                this.onViewClick();
13621                 //return true;
13622                 this.collapse();
13623                 
13624                 if(this.fireEvent("specialkey", this, e)){
13625                     this.onViewClick(false);
13626                 }
13627                 
13628                 return true;
13629             },
13630
13631             "esc" : function(e){
13632                 this.collapse();
13633             },
13634
13635             "tab" : function(e){
13636                 this.collapse();
13637                 
13638                 if(this.fireEvent("specialkey", this, e)){
13639                     this.onViewClick(false);
13640                 }
13641                 
13642                 return true;
13643             },
13644
13645             scope : this,
13646
13647             doRelay : function(foo, bar, hname){
13648                 if(hname == 'down' || this.scope.isExpanded()){
13649                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13650                 }
13651                 return true;
13652             },
13653
13654             forceKeyDown: true
13655         });
13656         
13657         
13658         this.queryDelay = Math.max(this.queryDelay || 10,
13659                 this.mode == 'local' ? 10 : 250);
13660         
13661         
13662         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13663         
13664         if(this.typeAhead){
13665             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13666         }
13667         if(this.editable !== false){
13668             this.inputEl().on("keyup", this.onKeyUp, this);
13669         }
13670         if(this.forceSelection){
13671             this.inputEl().on('blur', this.doForce, this);
13672         }
13673         
13674         if(this.multiple){
13675             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13676             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13677         }
13678     },
13679     
13680     initTickableEvents: function()
13681     {   
13682         this.createList();
13683         
13684         if(this.hiddenName){
13685             
13686             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13687             
13688             this.hiddenField.dom.value =
13689                 this.hiddenValue !== undefined ? this.hiddenValue :
13690                 this.value !== undefined ? this.value : '';
13691
13692             // prevent input submission
13693             this.el.dom.removeAttribute('name');
13694             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13695              
13696              
13697         }
13698         
13699 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13700         
13701         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13702         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13703         if(this.triggerList){
13704             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13705         }
13706          
13707         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13708         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13709         
13710         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13711         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13712         
13713         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13714         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13715         
13716         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13717         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13718         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13719         
13720         this.okBtn.hide();
13721         this.cancelBtn.hide();
13722         
13723         var _this = this;
13724         
13725         (function(){
13726             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13727             _this.list.setWidth(lw);
13728         }).defer(100);
13729         
13730         this.list.on('mouseover', this.onViewOver, this);
13731         this.list.on('mousemove', this.onViewMove, this);
13732         
13733         this.list.on('scroll', this.onViewScroll, this);
13734         
13735         if(!this.tpl){
13736             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13737                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13738         }
13739
13740         this.view = new Roo.View(this.list, this.tpl, {
13741             singleSelect:true,
13742             tickable:true,
13743             parent:this,
13744             store: this.store,
13745             selectedClass: this.selectedClass
13746         });
13747         
13748         //this.view.wrapEl.setDisplayed(false);
13749         this.view.on('click', this.onViewClick, this);
13750         
13751         
13752         
13753         this.store.on('beforeload', this.onBeforeLoad, this);
13754         this.store.on('load', this.onLoad, this);
13755         this.store.on('loadexception', this.onLoadException, this);
13756         
13757         if(this.editable){
13758             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13759                 "up" : function(e){
13760                     this.inKeyMode = true;
13761                     this.selectPrev();
13762                 },
13763
13764                 "down" : function(e){
13765                     this.inKeyMode = true;
13766                     this.selectNext();
13767                 },
13768
13769                 "enter" : function(e){
13770                     if(this.fireEvent("specialkey", this, e)){
13771                         this.onViewClick(false);
13772                     }
13773                     
13774                     return true;
13775                 },
13776
13777                 "esc" : function(e){
13778                     this.onTickableFooterButtonClick(e, false, false);
13779                 },
13780
13781                 "tab" : function(e){
13782                     this.fireEvent("specialkey", this, e);
13783                     
13784                     this.onTickableFooterButtonClick(e, false, false);
13785                     
13786                     return true;
13787                 },
13788
13789                 scope : this,
13790
13791                 doRelay : function(e, fn, key){
13792                     if(this.scope.isExpanded()){
13793                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13794                     }
13795                     return true;
13796                 },
13797
13798                 forceKeyDown: true
13799             });
13800         }
13801         
13802         this.queryDelay = Math.max(this.queryDelay || 10,
13803                 this.mode == 'local' ? 10 : 250);
13804         
13805         
13806         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13807         
13808         if(this.typeAhead){
13809             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13810         }
13811         
13812         if(this.editable !== false){
13813             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13814         }
13815         
13816         this.indicator = this.indicatorEl();
13817         
13818         if(this.indicator){
13819             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13820             this.indicator.hide();
13821         }
13822         
13823     },
13824
13825     onDestroy : function(){
13826         if(this.view){
13827             this.view.setStore(null);
13828             this.view.el.removeAllListeners();
13829             this.view.el.remove();
13830             this.view.purgeListeners();
13831         }
13832         if(this.list){
13833             this.list.dom.innerHTML  = '';
13834         }
13835         
13836         if(this.store){
13837             this.store.un('beforeload', this.onBeforeLoad, this);
13838             this.store.un('load', this.onLoad, this);
13839             this.store.un('loadexception', this.onLoadException, this);
13840         }
13841         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13842     },
13843
13844     // private
13845     fireKey : function(e){
13846         if(e.isNavKeyPress() && !this.list.isVisible()){
13847             this.fireEvent("specialkey", this, e);
13848         }
13849     },
13850
13851     // private
13852     onResize: function(w, h){
13853 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13854 //        
13855 //        if(typeof w != 'number'){
13856 //            // we do not handle it!?!?
13857 //            return;
13858 //        }
13859 //        var tw = this.trigger.getWidth();
13860 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13861 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13862 //        var x = w - tw;
13863 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13864 //            
13865 //        //this.trigger.setStyle('left', x+'px');
13866 //        
13867 //        if(this.list && this.listWidth === undefined){
13868 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13869 //            this.list.setWidth(lw);
13870 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13871 //        }
13872         
13873     
13874         
13875     },
13876
13877     /**
13878      * Allow or prevent the user from directly editing the field text.  If false is passed,
13879      * the user will only be able to select from the items defined in the dropdown list.  This method
13880      * is the runtime equivalent of setting the 'editable' config option at config time.
13881      * @param {Boolean} value True to allow the user to directly edit the field text
13882      */
13883     setEditable : function(value){
13884         if(value == this.editable){
13885             return;
13886         }
13887         this.editable = value;
13888         if(!value){
13889             this.inputEl().dom.setAttribute('readOnly', true);
13890             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13891             this.inputEl().addClass('x-combo-noedit');
13892         }else{
13893             this.inputEl().dom.setAttribute('readOnly', false);
13894             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13895             this.inputEl().removeClass('x-combo-noedit');
13896         }
13897     },
13898
13899     // private
13900     
13901     onBeforeLoad : function(combo,opts){
13902         if(!this.hasFocus){
13903             return;
13904         }
13905          if (!opts.add) {
13906             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13907          }
13908         this.restrictHeight();
13909         this.selectedIndex = -1;
13910     },
13911
13912     // private
13913     onLoad : function(){
13914         
13915         this.hasQuery = false;
13916         
13917         if(!this.hasFocus){
13918             return;
13919         }
13920         
13921         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13922             this.loading.hide();
13923         }
13924         
13925         if(this.store.getCount() > 0){
13926             
13927             this.expand();
13928             this.restrictHeight();
13929             if(this.lastQuery == this.allQuery){
13930                 if(this.editable && !this.tickable){
13931                     this.inputEl().dom.select();
13932                 }
13933                 
13934                 if(
13935                     !this.selectByValue(this.value, true) &&
13936                     this.autoFocus && 
13937                     (
13938                         !this.store.lastOptions ||
13939                         typeof(this.store.lastOptions.add) == 'undefined' || 
13940                         this.store.lastOptions.add != true
13941                     )
13942                 ){
13943                     this.select(0, true);
13944                 }
13945             }else{
13946                 if(this.autoFocus){
13947                     this.selectNext();
13948                 }
13949                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13950                     this.taTask.delay(this.typeAheadDelay);
13951                 }
13952             }
13953         }else{
13954             this.onEmptyResults();
13955         }
13956         
13957         //this.el.focus();
13958     },
13959     // private
13960     onLoadException : function()
13961     {
13962         this.hasQuery = false;
13963         
13964         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13965             this.loading.hide();
13966         }
13967         
13968         if(this.tickable && this.editable){
13969             return;
13970         }
13971         
13972         this.collapse();
13973         // only causes errors at present
13974         //Roo.log(this.store.reader.jsonData);
13975         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13976             // fixme
13977             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13978         //}
13979         
13980         
13981     },
13982     // private
13983     onTypeAhead : function(){
13984         if(this.store.getCount() > 0){
13985             var r = this.store.getAt(0);
13986             var newValue = r.data[this.displayField];
13987             var len = newValue.length;
13988             var selStart = this.getRawValue().length;
13989             
13990             if(selStart != len){
13991                 this.setRawValue(newValue);
13992                 this.selectText(selStart, newValue.length);
13993             }
13994         }
13995     },
13996
13997     // private
13998     onSelect : function(record, index){
13999         
14000         if(this.fireEvent('beforeselect', this, record, index) !== false){
14001         
14002             this.setFromData(index > -1 ? record.data : false);
14003             
14004             this.collapse();
14005             this.fireEvent('select', this, record, index);
14006         }
14007     },
14008
14009     /**
14010      * Returns the currently selected field value or empty string if no value is set.
14011      * @return {String} value The selected value
14012      */
14013     getValue : function()
14014     {
14015         if(Roo.isIOS && this.useNativeIOS){
14016             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14017         }
14018         
14019         if(this.multiple){
14020             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14021         }
14022         
14023         if(this.valueField){
14024             return typeof this.value != 'undefined' ? this.value : '';
14025         }else{
14026             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14027         }
14028     },
14029     
14030     getRawValue : function()
14031     {
14032         if(Roo.isIOS && this.useNativeIOS){
14033             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14034         }
14035         
14036         var v = this.inputEl().getValue();
14037         
14038         return v;
14039     },
14040
14041     /**
14042      * Clears any text/value currently set in the field
14043      */
14044     clearValue : function(){
14045         
14046         if(this.hiddenField){
14047             this.hiddenField.dom.value = '';
14048         }
14049         this.value = '';
14050         this.setRawValue('');
14051         this.lastSelectionText = '';
14052         this.lastData = false;
14053         
14054         var close = this.closeTriggerEl();
14055         
14056         if(close){
14057             close.hide();
14058         }
14059         
14060         this.validate();
14061         
14062     },
14063
14064     /**
14065      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14066      * will be displayed in the field.  If the value does not match the data value of an existing item,
14067      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14068      * Otherwise the field will be blank (although the value will still be set).
14069      * @param {String} value The value to match
14070      */
14071     setValue : function(v)
14072     {
14073         if(Roo.isIOS && this.useNativeIOS){
14074             this.setIOSValue(v);
14075             return;
14076         }
14077         
14078         if(this.multiple){
14079             this.syncValue();
14080             return;
14081         }
14082         
14083         var text = v;
14084         if(this.valueField){
14085             var r = this.findRecord(this.valueField, v);
14086             if(r){
14087                 text = r.data[this.displayField];
14088             }else if(this.valueNotFoundText !== undefined){
14089                 text = this.valueNotFoundText;
14090             }
14091         }
14092         this.lastSelectionText = text;
14093         if(this.hiddenField){
14094             this.hiddenField.dom.value = v;
14095         }
14096         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14097         this.value = v;
14098         
14099         var close = this.closeTriggerEl();
14100         
14101         if(close){
14102             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14103         }
14104         
14105         this.validate();
14106     },
14107     /**
14108      * @property {Object} the last set data for the element
14109      */
14110     
14111     lastData : false,
14112     /**
14113      * Sets the value of the field based on a object which is related to the record format for the store.
14114      * @param {Object} value the value to set as. or false on reset?
14115      */
14116     setFromData : function(o){
14117         
14118         if(this.multiple){
14119             this.addItem(o);
14120             return;
14121         }
14122             
14123         var dv = ''; // display value
14124         var vv = ''; // value value..
14125         this.lastData = o;
14126         if (this.displayField) {
14127             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14128         } else {
14129             // this is an error condition!!!
14130             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14131         }
14132         
14133         if(this.valueField){
14134             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14135         }
14136         
14137         var close = this.closeTriggerEl();
14138         
14139         if(close){
14140             if(dv.length || vv * 1 > 0){
14141                 close.show() ;
14142                 this.blockFocus=true;
14143             } else {
14144                 close.hide();
14145             }             
14146         }
14147         
14148         if(this.hiddenField){
14149             this.hiddenField.dom.value = vv;
14150             
14151             this.lastSelectionText = dv;
14152             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14153             this.value = vv;
14154             return;
14155         }
14156         // no hidden field.. - we store the value in 'value', but still display
14157         // display field!!!!
14158         this.lastSelectionText = dv;
14159         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14160         this.value = vv;
14161         
14162         
14163         
14164     },
14165     // private
14166     reset : function(){
14167         // overridden so that last data is reset..
14168         
14169         if(this.multiple){
14170             this.clearItem();
14171             return;
14172         }
14173         
14174         this.setValue(this.originalValue);
14175         //this.clearInvalid();
14176         this.lastData = false;
14177         if (this.view) {
14178             this.view.clearSelections();
14179         }
14180         
14181         this.validate();
14182     },
14183     // private
14184     findRecord : function(prop, value){
14185         var record;
14186         if(this.store.getCount() > 0){
14187             this.store.each(function(r){
14188                 if(r.data[prop] == value){
14189                     record = r;
14190                     return false;
14191                 }
14192                 return true;
14193             });
14194         }
14195         return record;
14196     },
14197     
14198     getName: function()
14199     {
14200         // returns hidden if it's set..
14201         if (!this.rendered) {return ''};
14202         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14203         
14204     },
14205     // private
14206     onViewMove : function(e, t){
14207         this.inKeyMode = false;
14208     },
14209
14210     // private
14211     onViewOver : function(e, t){
14212         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14213             return;
14214         }
14215         var item = this.view.findItemFromChild(t);
14216         
14217         if(item){
14218             var index = this.view.indexOf(item);
14219             this.select(index, false);
14220         }
14221     },
14222
14223     // private
14224     onViewClick : function(view, doFocus, el, e)
14225     {
14226         var index = this.view.getSelectedIndexes()[0];
14227         
14228         var r = this.store.getAt(index);
14229         
14230         if(this.tickable){
14231             
14232             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14233                 return;
14234             }
14235             
14236             var rm = false;
14237             var _this = this;
14238             
14239             Roo.each(this.tickItems, function(v,k){
14240                 
14241                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14242                     Roo.log(v);
14243                     _this.tickItems.splice(k, 1);
14244                     
14245                     if(typeof(e) == 'undefined' && view == false){
14246                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14247                     }
14248                     
14249                     rm = true;
14250                     return;
14251                 }
14252             });
14253             
14254             if(rm){
14255                 return;
14256             }
14257             
14258             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14259                 this.tickItems.push(r.data);
14260             }
14261             
14262             if(typeof(e) == 'undefined' && view == false){
14263                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14264             }
14265                     
14266             return;
14267         }
14268         
14269         if(r){
14270             this.onSelect(r, index);
14271         }
14272         if(doFocus !== false && !this.blockFocus){
14273             this.inputEl().focus();
14274         }
14275     },
14276
14277     // private
14278     restrictHeight : function(){
14279         //this.innerList.dom.style.height = '';
14280         //var inner = this.innerList.dom;
14281         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14282         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14283         //this.list.beginUpdate();
14284         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14285         this.list.alignTo(this.inputEl(), this.listAlign);
14286         this.list.alignTo(this.inputEl(), this.listAlign);
14287         //this.list.endUpdate();
14288     },
14289
14290     // private
14291     onEmptyResults : function(){
14292         
14293         if(this.tickable && this.editable){
14294             this.hasFocus = false;
14295             this.restrictHeight();
14296             return;
14297         }
14298         
14299         this.collapse();
14300     },
14301
14302     /**
14303      * Returns true if the dropdown list is expanded, else false.
14304      */
14305     isExpanded : function(){
14306         return this.list.isVisible();
14307     },
14308
14309     /**
14310      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14311      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14312      * @param {String} value The data value of the item to select
14313      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14314      * selected item if it is not currently in view (defaults to true)
14315      * @return {Boolean} True if the value matched an item in the list, else false
14316      */
14317     selectByValue : function(v, scrollIntoView){
14318         if(v !== undefined && v !== null){
14319             var r = this.findRecord(this.valueField || this.displayField, v);
14320             if(r){
14321                 this.select(this.store.indexOf(r), scrollIntoView);
14322                 return true;
14323             }
14324         }
14325         return false;
14326     },
14327
14328     /**
14329      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14330      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14331      * @param {Number} index The zero-based index of the list item to select
14332      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14333      * selected item if it is not currently in view (defaults to true)
14334      */
14335     select : function(index, scrollIntoView){
14336         this.selectedIndex = index;
14337         this.view.select(index);
14338         if(scrollIntoView !== false){
14339             var el = this.view.getNode(index);
14340             /*
14341              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14342              */
14343             if(el){
14344                 this.list.scrollChildIntoView(el, false);
14345             }
14346         }
14347     },
14348
14349     // private
14350     selectNext : function(){
14351         var ct = this.store.getCount();
14352         if(ct > 0){
14353             if(this.selectedIndex == -1){
14354                 this.select(0);
14355             }else if(this.selectedIndex < ct-1){
14356                 this.select(this.selectedIndex+1);
14357             }
14358         }
14359     },
14360
14361     // private
14362     selectPrev : function(){
14363         var ct = this.store.getCount();
14364         if(ct > 0){
14365             if(this.selectedIndex == -1){
14366                 this.select(0);
14367             }else if(this.selectedIndex != 0){
14368                 this.select(this.selectedIndex-1);
14369             }
14370         }
14371     },
14372
14373     // private
14374     onKeyUp : function(e){
14375         if(this.editable !== false && !e.isSpecialKey()){
14376             this.lastKey = e.getKey();
14377             this.dqTask.delay(this.queryDelay);
14378         }
14379     },
14380
14381     // private
14382     validateBlur : function(){
14383         return !this.list || !this.list.isVisible();   
14384     },
14385
14386     // private
14387     initQuery : function(){
14388         
14389         var v = this.getRawValue();
14390         
14391         if(this.tickable && this.editable){
14392             v = this.tickableInputEl().getValue();
14393         }
14394         
14395         this.doQuery(v);
14396     },
14397
14398     // private
14399     doForce : function(){
14400         if(this.inputEl().dom.value.length > 0){
14401             this.inputEl().dom.value =
14402                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14403              
14404         }
14405     },
14406
14407     /**
14408      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14409      * query allowing the query action to be canceled if needed.
14410      * @param {String} query The SQL query to execute
14411      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14412      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14413      * saved in the current store (defaults to false)
14414      */
14415     doQuery : function(q, forceAll){
14416         
14417         if(q === undefined || q === null){
14418             q = '';
14419         }
14420         var qe = {
14421             query: q,
14422             forceAll: forceAll,
14423             combo: this,
14424             cancel:false
14425         };
14426         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14427             return false;
14428         }
14429         q = qe.query;
14430         
14431         forceAll = qe.forceAll;
14432         if(forceAll === true || (q.length >= this.minChars)){
14433             
14434             this.hasQuery = true;
14435             
14436             if(this.lastQuery != q || this.alwaysQuery){
14437                 this.lastQuery = q;
14438                 if(this.mode == 'local'){
14439                     this.selectedIndex = -1;
14440                     if(forceAll){
14441                         this.store.clearFilter();
14442                     }else{
14443                         
14444                         if(this.specialFilter){
14445                             this.fireEvent('specialfilter', this);
14446                             this.onLoad();
14447                             return;
14448                         }
14449                         
14450                         this.store.filter(this.displayField, q);
14451                     }
14452                     
14453                     this.store.fireEvent("datachanged", this.store);
14454                     
14455                     this.onLoad();
14456                     
14457                     
14458                 }else{
14459                     
14460                     this.store.baseParams[this.queryParam] = q;
14461                     
14462                     var options = {params : this.getParams(q)};
14463                     
14464                     if(this.loadNext){
14465                         options.add = true;
14466                         options.params.start = this.page * this.pageSize;
14467                     }
14468                     
14469                     this.store.load(options);
14470                     
14471                     /*
14472                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14473                      *  we should expand the list on onLoad
14474                      *  so command out it
14475                      */
14476 //                    this.expand();
14477                 }
14478             }else{
14479                 this.selectedIndex = -1;
14480                 this.onLoad();   
14481             }
14482         }
14483         
14484         this.loadNext = false;
14485     },
14486     
14487     // private
14488     getParams : function(q){
14489         var p = {};
14490         //p[this.queryParam] = q;
14491         
14492         if(this.pageSize){
14493             p.start = 0;
14494             p.limit = this.pageSize;
14495         }
14496         return p;
14497     },
14498
14499     /**
14500      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14501      */
14502     collapse : function(){
14503         if(!this.isExpanded()){
14504             return;
14505         }
14506         
14507         this.list.hide();
14508         
14509         this.hasFocus = false;
14510         
14511         if(this.tickable){
14512             this.okBtn.hide();
14513             this.cancelBtn.hide();
14514             this.trigger.show();
14515             
14516             if(this.editable){
14517                 this.tickableInputEl().dom.value = '';
14518                 this.tickableInputEl().blur();
14519             }
14520             
14521         }
14522         
14523         Roo.get(document).un('mousedown', this.collapseIf, this);
14524         Roo.get(document).un('mousewheel', this.collapseIf, this);
14525         if (!this.editable) {
14526             Roo.get(document).un('keydown', this.listKeyPress, this);
14527         }
14528         this.fireEvent('collapse', this);
14529         
14530         this.validate();
14531     },
14532
14533     // private
14534     collapseIf : function(e){
14535         var in_combo  = e.within(this.el);
14536         var in_list =  e.within(this.list);
14537         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14538         
14539         if (in_combo || in_list || is_list) {
14540             //e.stopPropagation();
14541             return;
14542         }
14543         
14544         if(this.tickable){
14545             this.onTickableFooterButtonClick(e, false, false);
14546         }
14547
14548         this.collapse();
14549         
14550     },
14551
14552     /**
14553      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14554      */
14555     expand : function(){
14556        
14557         if(this.isExpanded() || !this.hasFocus){
14558             return;
14559         }
14560         
14561         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14562         this.list.setWidth(lw);
14563         
14564         Roo.log('expand');
14565         
14566         this.list.show();
14567         
14568         this.restrictHeight();
14569         
14570         if(this.tickable){
14571             
14572             this.tickItems = Roo.apply([], this.item);
14573             
14574             this.okBtn.show();
14575             this.cancelBtn.show();
14576             this.trigger.hide();
14577             
14578             if(this.editable){
14579                 this.tickableInputEl().focus();
14580             }
14581             
14582         }
14583         
14584         Roo.get(document).on('mousedown', this.collapseIf, this);
14585         Roo.get(document).on('mousewheel', this.collapseIf, this);
14586         if (!this.editable) {
14587             Roo.get(document).on('keydown', this.listKeyPress, this);
14588         }
14589         
14590         this.fireEvent('expand', this);
14591     },
14592
14593     // private
14594     // Implements the default empty TriggerField.onTriggerClick function
14595     onTriggerClick : function(e)
14596     {
14597         Roo.log('trigger click');
14598         
14599         if(this.disabled || !this.triggerList){
14600             return;
14601         }
14602         
14603         this.page = 0;
14604         this.loadNext = false;
14605         
14606         if(this.isExpanded()){
14607             this.collapse();
14608             if (!this.blockFocus) {
14609                 this.inputEl().focus();
14610             }
14611             
14612         }else {
14613             this.hasFocus = true;
14614             if(this.triggerAction == 'all') {
14615                 this.doQuery(this.allQuery, true);
14616             } else {
14617                 this.doQuery(this.getRawValue());
14618             }
14619             if (!this.blockFocus) {
14620                 this.inputEl().focus();
14621             }
14622         }
14623     },
14624     
14625     onTickableTriggerClick : function(e)
14626     {
14627         if(this.disabled){
14628             return;
14629         }
14630         
14631         this.page = 0;
14632         this.loadNext = false;
14633         this.hasFocus = true;
14634         
14635         if(this.triggerAction == 'all') {
14636             this.doQuery(this.allQuery, true);
14637         } else {
14638             this.doQuery(this.getRawValue());
14639         }
14640     },
14641     
14642     onSearchFieldClick : function(e)
14643     {
14644         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14645             this.onTickableFooterButtonClick(e, false, false);
14646             return;
14647         }
14648         
14649         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14650             return;
14651         }
14652         
14653         this.page = 0;
14654         this.loadNext = false;
14655         this.hasFocus = true;
14656         
14657         if(this.triggerAction == 'all') {
14658             this.doQuery(this.allQuery, true);
14659         } else {
14660             this.doQuery(this.getRawValue());
14661         }
14662     },
14663     
14664     listKeyPress : function(e)
14665     {
14666         //Roo.log('listkeypress');
14667         // scroll to first matching element based on key pres..
14668         if (e.isSpecialKey()) {
14669             return false;
14670         }
14671         var k = String.fromCharCode(e.getKey()).toUpperCase();
14672         //Roo.log(k);
14673         var match  = false;
14674         var csel = this.view.getSelectedNodes();
14675         var cselitem = false;
14676         if (csel.length) {
14677             var ix = this.view.indexOf(csel[0]);
14678             cselitem  = this.store.getAt(ix);
14679             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14680                 cselitem = false;
14681             }
14682             
14683         }
14684         
14685         this.store.each(function(v) { 
14686             if (cselitem) {
14687                 // start at existing selection.
14688                 if (cselitem.id == v.id) {
14689                     cselitem = false;
14690                 }
14691                 return true;
14692             }
14693                 
14694             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14695                 match = this.store.indexOf(v);
14696                 return false;
14697             }
14698             return true;
14699         }, this);
14700         
14701         if (match === false) {
14702             return true; // no more action?
14703         }
14704         // scroll to?
14705         this.view.select(match);
14706         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14707         sn.scrollIntoView(sn.dom.parentNode, false);
14708     },
14709     
14710     onViewScroll : function(e, t){
14711         
14712         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){
14713             return;
14714         }
14715         
14716         this.hasQuery = true;
14717         
14718         this.loading = this.list.select('.loading', true).first();
14719         
14720         if(this.loading === null){
14721             this.list.createChild({
14722                 tag: 'div',
14723                 cls: 'loading roo-select2-more-results roo-select2-active',
14724                 html: 'Loading more results...'
14725             });
14726             
14727             this.loading = this.list.select('.loading', true).first();
14728             
14729             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14730             
14731             this.loading.hide();
14732         }
14733         
14734         this.loading.show();
14735         
14736         var _combo = this;
14737         
14738         this.page++;
14739         this.loadNext = true;
14740         
14741         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14742         
14743         return;
14744     },
14745     
14746     addItem : function(o)
14747     {   
14748         var dv = ''; // display value
14749         
14750         if (this.displayField) {
14751             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14752         } else {
14753             // this is an error condition!!!
14754             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14755         }
14756         
14757         if(!dv.length){
14758             return;
14759         }
14760         
14761         var choice = this.choices.createChild({
14762             tag: 'li',
14763             cls: 'roo-select2-search-choice',
14764             cn: [
14765                 {
14766                     tag: 'div',
14767                     html: dv
14768                 },
14769                 {
14770                     tag: 'a',
14771                     href: '#',
14772                     cls: 'roo-select2-search-choice-close fa fa-times',
14773                     tabindex: '-1'
14774                 }
14775             ]
14776             
14777         }, this.searchField);
14778         
14779         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14780         
14781         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14782         
14783         this.item.push(o);
14784         
14785         this.lastData = o;
14786         
14787         this.syncValue();
14788         
14789         this.inputEl().dom.value = '';
14790         
14791         this.validate();
14792     },
14793     
14794     onRemoveItem : function(e, _self, o)
14795     {
14796         e.preventDefault();
14797         
14798         this.lastItem = Roo.apply([], this.item);
14799         
14800         var index = this.item.indexOf(o.data) * 1;
14801         
14802         if( index < 0){
14803             Roo.log('not this item?!');
14804             return;
14805         }
14806         
14807         this.item.splice(index, 1);
14808         o.item.remove();
14809         
14810         this.syncValue();
14811         
14812         this.fireEvent('remove', this, e);
14813         
14814         this.validate();
14815         
14816     },
14817     
14818     syncValue : function()
14819     {
14820         if(!this.item.length){
14821             this.clearValue();
14822             return;
14823         }
14824             
14825         var value = [];
14826         var _this = this;
14827         Roo.each(this.item, function(i){
14828             if(_this.valueField){
14829                 value.push(i[_this.valueField]);
14830                 return;
14831             }
14832
14833             value.push(i);
14834         });
14835
14836         this.value = value.join(',');
14837
14838         if(this.hiddenField){
14839             this.hiddenField.dom.value = this.value;
14840         }
14841         
14842         this.store.fireEvent("datachanged", this.store);
14843         
14844         this.validate();
14845     },
14846     
14847     clearItem : function()
14848     {
14849         if(!this.multiple){
14850             return;
14851         }
14852         
14853         this.item = [];
14854         
14855         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14856            c.remove();
14857         });
14858         
14859         this.syncValue();
14860         
14861         this.validate();
14862         
14863         if(this.tickable && !Roo.isTouch){
14864             this.view.refresh();
14865         }
14866     },
14867     
14868     inputEl: function ()
14869     {
14870         if(Roo.isIOS && this.useNativeIOS){
14871             return this.el.select('select.roo-ios-select', true).first();
14872         }
14873         
14874         if(Roo.isTouch && this.mobileTouchView){
14875             return this.el.select('input.form-control',true).first();
14876         }
14877         
14878         if(this.tickable){
14879             return this.searchField;
14880         }
14881         
14882         return this.el.select('input.form-control',true).first();
14883     },
14884     
14885     onTickableFooterButtonClick : function(e, btn, el)
14886     {
14887         e.preventDefault();
14888         
14889         this.lastItem = Roo.apply([], this.item);
14890         
14891         if(btn && btn.name == 'cancel'){
14892             this.tickItems = Roo.apply([], this.item);
14893             this.collapse();
14894             return;
14895         }
14896         
14897         this.clearItem();
14898         
14899         var _this = this;
14900         
14901         Roo.each(this.tickItems, function(o){
14902             _this.addItem(o);
14903         });
14904         
14905         this.collapse();
14906         
14907     },
14908     
14909     validate : function()
14910     {
14911         if(this.getVisibilityEl().hasClass('hidden')){
14912             return true;
14913         }
14914         
14915         var v = this.getRawValue();
14916         
14917         if(this.multiple){
14918             v = this.getValue();
14919         }
14920         
14921         if(this.disabled || this.allowBlank || v.length){
14922             this.markValid();
14923             return true;
14924         }
14925         
14926         this.markInvalid();
14927         return false;
14928     },
14929     
14930     tickableInputEl : function()
14931     {
14932         if(!this.tickable || !this.editable){
14933             return this.inputEl();
14934         }
14935         
14936         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14937     },
14938     
14939     
14940     getAutoCreateTouchView : function()
14941     {
14942         var id = Roo.id();
14943         
14944         var cfg = {
14945             cls: 'form-group' //input-group
14946         };
14947         
14948         var input =  {
14949             tag: 'input',
14950             id : id,
14951             type : this.inputType,
14952             cls : 'form-control x-combo-noedit',
14953             autocomplete: 'new-password',
14954             placeholder : this.placeholder || '',
14955             readonly : true
14956         };
14957         
14958         if (this.name) {
14959             input.name = this.name;
14960         }
14961         
14962         if (this.size) {
14963             input.cls += ' input-' + this.size;
14964         }
14965         
14966         if (this.disabled) {
14967             input.disabled = true;
14968         }
14969         
14970         var inputblock = {
14971             cls : '',
14972             cn : [
14973                 input
14974             ]
14975         };
14976         
14977         if(this.before){
14978             inputblock.cls += ' input-group';
14979             
14980             inputblock.cn.unshift({
14981                 tag :'span',
14982                 cls : 'input-group-addon',
14983                 html : this.before
14984             });
14985         }
14986         
14987         if(this.removable && !this.multiple){
14988             inputblock.cls += ' roo-removable';
14989             
14990             inputblock.cn.push({
14991                 tag: 'button',
14992                 html : 'x',
14993                 cls : 'roo-combo-removable-btn close'
14994             });
14995         }
14996
14997         if(this.hasFeedback && !this.allowBlank){
14998             
14999             inputblock.cls += ' has-feedback';
15000             
15001             inputblock.cn.push({
15002                 tag: 'span',
15003                 cls: 'glyphicon form-control-feedback'
15004             });
15005             
15006         }
15007         
15008         if (this.after) {
15009             
15010             inputblock.cls += (this.before) ? '' : ' input-group';
15011             
15012             inputblock.cn.push({
15013                 tag :'span',
15014                 cls : 'input-group-addon',
15015                 html : this.after
15016             });
15017         }
15018
15019         var box = {
15020             tag: 'div',
15021             cn: [
15022                 {
15023                     tag: 'input',
15024                     type : 'hidden',
15025                     cls: 'form-hidden-field'
15026                 },
15027                 inputblock
15028             ]
15029             
15030         };
15031         
15032         if(this.multiple){
15033             box = {
15034                 tag: 'div',
15035                 cn: [
15036                     {
15037                         tag: 'input',
15038                         type : 'hidden',
15039                         cls: 'form-hidden-field'
15040                     },
15041                     {
15042                         tag: 'ul',
15043                         cls: 'roo-select2-choices',
15044                         cn:[
15045                             {
15046                                 tag: 'li',
15047                                 cls: 'roo-select2-search-field',
15048                                 cn: [
15049
15050                                     inputblock
15051                                 ]
15052                             }
15053                         ]
15054                     }
15055                 ]
15056             }
15057         };
15058         
15059         var combobox = {
15060             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15061             cn: [
15062                 box
15063             ]
15064         };
15065         
15066         if(!this.multiple && this.showToggleBtn){
15067             
15068             var caret = {
15069                         tag: 'span',
15070                         cls: 'caret'
15071             };
15072             
15073             if (this.caret != false) {
15074                 caret = {
15075                      tag: 'i',
15076                      cls: 'fa fa-' + this.caret
15077                 };
15078                 
15079             }
15080             
15081             combobox.cn.push({
15082                 tag :'span',
15083                 cls : 'input-group-addon btn dropdown-toggle',
15084                 cn : [
15085                     caret,
15086                     {
15087                         tag: 'span',
15088                         cls: 'combobox-clear',
15089                         cn  : [
15090                             {
15091                                 tag : 'i',
15092                                 cls: 'icon-remove'
15093                             }
15094                         ]
15095                     }
15096                 ]
15097
15098             })
15099         }
15100         
15101         if(this.multiple){
15102             combobox.cls += ' roo-select2-container-multi';
15103         }
15104         
15105         var align = this.labelAlign || this.parentLabelAlign();
15106         
15107         if (align ==='left' && this.fieldLabel.length) {
15108
15109             cfg.cn = [
15110                 {
15111                    tag : 'i',
15112                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15113                    tooltip : 'This field is required'
15114                 },
15115                 {
15116                     tag: 'label',
15117                     cls : 'control-label',
15118                     html : this.fieldLabel
15119
15120                 },
15121                 {
15122                     cls : '', 
15123                     cn: [
15124                         combobox
15125                     ]
15126                 }
15127             ];
15128             
15129             var labelCfg = cfg.cn[1];
15130             var contentCfg = cfg.cn[2];
15131             
15132
15133             if(this.indicatorpos == 'right'){
15134                 cfg.cn = [
15135                     {
15136                         tag: 'label',
15137                         'for' :  id,
15138                         cls : 'control-label',
15139                         cn : [
15140                             {
15141                                 tag : 'span',
15142                                 html : this.fieldLabel
15143                             },
15144                             {
15145                                 tag : 'i',
15146                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15147                                 tooltip : 'This field is required'
15148                             }
15149                         ]
15150                     },
15151                     {
15152                         cls : "",
15153                         cn: [
15154                             combobox
15155                         ]
15156                     }
15157
15158                 ];
15159                 
15160                 labelCfg = cfg.cn[0];
15161                 contentCfg = cfg.cn[1];
15162             }
15163             
15164            
15165             
15166             if(this.labelWidth > 12){
15167                 labelCfg.style = "width: " + this.labelWidth + 'px';
15168             }
15169             
15170             if(this.labelWidth < 13 && this.labelmd == 0){
15171                 this.labelmd = this.labelWidth;
15172             }
15173             
15174             if(this.labellg > 0){
15175                 labelCfg.cls += ' col-lg-' + this.labellg;
15176                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15177             }
15178             
15179             if(this.labelmd > 0){
15180                 labelCfg.cls += ' col-md-' + this.labelmd;
15181                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15182             }
15183             
15184             if(this.labelsm > 0){
15185                 labelCfg.cls += ' col-sm-' + this.labelsm;
15186                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15187             }
15188             
15189             if(this.labelxs > 0){
15190                 labelCfg.cls += ' col-xs-' + this.labelxs;
15191                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15192             }
15193                 
15194                 
15195         } else if ( this.fieldLabel.length) {
15196             cfg.cn = [
15197                 {
15198                    tag : 'i',
15199                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15200                    tooltip : 'This field is required'
15201                 },
15202                 {
15203                     tag: 'label',
15204                     cls : 'control-label',
15205                     html : this.fieldLabel
15206
15207                 },
15208                 {
15209                     cls : '', 
15210                     cn: [
15211                         combobox
15212                     ]
15213                 }
15214             ];
15215             
15216             if(this.indicatorpos == 'right'){
15217                 cfg.cn = [
15218                     {
15219                         tag: 'label',
15220                         cls : 'control-label',
15221                         html : this.fieldLabel,
15222                         cn : [
15223                             {
15224                                tag : 'i',
15225                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15226                                tooltip : 'This field is required'
15227                             }
15228                         ]
15229                     },
15230                     {
15231                         cls : '', 
15232                         cn: [
15233                             combobox
15234                         ]
15235                     }
15236                 ];
15237             }
15238         } else {
15239             cfg.cn = combobox;    
15240         }
15241         
15242         
15243         var settings = this;
15244         
15245         ['xs','sm','md','lg'].map(function(size){
15246             if (settings[size]) {
15247                 cfg.cls += ' col-' + size + '-' + settings[size];
15248             }
15249         });
15250         
15251         return cfg;
15252     },
15253     
15254     initTouchView : function()
15255     {
15256         this.renderTouchView();
15257         
15258         this.touchViewEl.on('scroll', function(){
15259             this.el.dom.scrollTop = 0;
15260         }, this);
15261         
15262         this.originalValue = this.getValue();
15263         
15264         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15265         
15266         this.inputEl().on("click", this.showTouchView, this);
15267         if (this.triggerEl) {
15268             this.triggerEl.on("click", this.showTouchView, this);
15269         }
15270         
15271         
15272         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15273         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15274         
15275         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15276         
15277         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15278         this.store.on('load', this.onTouchViewLoad, this);
15279         this.store.on('loadexception', this.onTouchViewLoadException, this);
15280         
15281         if(this.hiddenName){
15282             
15283             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15284             
15285             this.hiddenField.dom.value =
15286                 this.hiddenValue !== undefined ? this.hiddenValue :
15287                 this.value !== undefined ? this.value : '';
15288         
15289             this.el.dom.removeAttribute('name');
15290             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15291         }
15292         
15293         if(this.multiple){
15294             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15295             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15296         }
15297         
15298         if(this.removable && !this.multiple){
15299             var close = this.closeTriggerEl();
15300             if(close){
15301                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15302                 close.on('click', this.removeBtnClick, this, close);
15303             }
15304         }
15305         /*
15306          * fix the bug in Safari iOS8
15307          */
15308         this.inputEl().on("focus", function(e){
15309             document.activeElement.blur();
15310         }, this);
15311         
15312         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15313         
15314         return;
15315         
15316         
15317     },
15318     
15319     renderTouchView : function()
15320     {
15321         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15322         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15323         
15324         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15325         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15326         
15327         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15328         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15329         this.touchViewBodyEl.setStyle('overflow', 'auto');
15330         
15331         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15332         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15333         
15334         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15335         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15336         
15337     },
15338     
15339     showTouchView : function()
15340     {
15341         if(this.disabled){
15342             return;
15343         }
15344         
15345         this.touchViewHeaderEl.hide();
15346
15347         if(this.modalTitle.length){
15348             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15349             this.touchViewHeaderEl.show();
15350         }
15351
15352         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15353         this.touchViewEl.show();
15354
15355         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15356         
15357         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15358         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15359
15360         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15361
15362         if(this.modalTitle.length){
15363             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15364         }
15365         
15366         this.touchViewBodyEl.setHeight(bodyHeight);
15367
15368         if(this.animate){
15369             var _this = this;
15370             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15371         }else{
15372             this.touchViewEl.addClass('in');
15373         }
15374         
15375         if(this._touchViewMask){
15376             Roo.get(document.body).addClass("x-body-masked");
15377             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15378             this._touchViewMask.setStyle('z-index', 10000);
15379             this._touchViewMask.addClass('show');
15380         }
15381         
15382         this.doTouchViewQuery();
15383         
15384     },
15385     
15386     hideTouchView : function()
15387     {
15388         this.touchViewEl.removeClass('in');
15389
15390         if(this.animate){
15391             var _this = this;
15392             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15393         }else{
15394             this.touchViewEl.setStyle('display', 'none');
15395         }
15396         
15397         if(this._touchViewMask){
15398             this._touchViewMask.removeClass('show');
15399             Roo.get(document.body).removeClass("x-body-masked");
15400         }
15401     },
15402     
15403     setTouchViewValue : function()
15404     {
15405         if(this.multiple){
15406             this.clearItem();
15407         
15408             var _this = this;
15409
15410             Roo.each(this.tickItems, function(o){
15411                 this.addItem(o);
15412             }, this);
15413         }
15414         
15415         this.hideTouchView();
15416     },
15417     
15418     doTouchViewQuery : function()
15419     {
15420         var qe = {
15421             query: '',
15422             forceAll: true,
15423             combo: this,
15424             cancel:false
15425         };
15426         
15427         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15428             return false;
15429         }
15430         
15431         if(!this.alwaysQuery || this.mode == 'local'){
15432             this.onTouchViewLoad();
15433             return;
15434         }
15435         
15436         this.store.load();
15437     },
15438     
15439     onTouchViewBeforeLoad : function(combo,opts)
15440     {
15441         return;
15442     },
15443
15444     // private
15445     onTouchViewLoad : function()
15446     {
15447         if(this.store.getCount() < 1){
15448             this.onTouchViewEmptyResults();
15449             return;
15450         }
15451         
15452         this.clearTouchView();
15453         
15454         var rawValue = this.getRawValue();
15455         
15456         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15457         
15458         this.tickItems = [];
15459         
15460         this.store.data.each(function(d, rowIndex){
15461             var row = this.touchViewListGroup.createChild(template);
15462             
15463             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15464                 row.addClass(d.data.cls);
15465             }
15466             
15467             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15468                 var cfg = {
15469                     data : d.data,
15470                     html : d.data[this.displayField]
15471                 };
15472                 
15473                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15474                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15475                 }
15476             }
15477             row.removeClass('selected');
15478             if(!this.multiple && this.valueField &&
15479                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15480             {
15481                 // radio buttons..
15482                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15483                 row.addClass('selected');
15484             }
15485             
15486             if(this.multiple && this.valueField &&
15487                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15488             {
15489                 
15490                 // checkboxes...
15491                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15492                 this.tickItems.push(d.data);
15493             }
15494             
15495             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15496             
15497         }, this);
15498         
15499         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15500         
15501         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15502
15503         if(this.modalTitle.length){
15504             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15505         }
15506
15507         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15508         
15509         if(this.mobile_restrict_height && listHeight < bodyHeight){
15510             this.touchViewBodyEl.setHeight(listHeight);
15511         }
15512         
15513         var _this = this;
15514         
15515         if(firstChecked && listHeight > bodyHeight){
15516             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15517         }
15518         
15519     },
15520     
15521     onTouchViewLoadException : function()
15522     {
15523         this.hideTouchView();
15524     },
15525     
15526     onTouchViewEmptyResults : function()
15527     {
15528         this.clearTouchView();
15529         
15530         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15531         
15532         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15533         
15534     },
15535     
15536     clearTouchView : function()
15537     {
15538         this.touchViewListGroup.dom.innerHTML = '';
15539     },
15540     
15541     onTouchViewClick : function(e, el, o)
15542     {
15543         e.preventDefault();
15544         
15545         var row = o.row;
15546         var rowIndex = o.rowIndex;
15547         
15548         var r = this.store.getAt(rowIndex);
15549         
15550         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15551             
15552             if(!this.multiple){
15553                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15554                     c.dom.removeAttribute('checked');
15555                 }, this);
15556
15557                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15558
15559                 this.setFromData(r.data);
15560
15561                 var close = this.closeTriggerEl();
15562
15563                 if(close){
15564                     close.show();
15565                 }
15566
15567                 this.hideTouchView();
15568
15569                 this.fireEvent('select', this, r, rowIndex);
15570
15571                 return;
15572             }
15573
15574             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15575                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15576                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15577                 return;
15578             }
15579
15580             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15581             this.addItem(r.data);
15582             this.tickItems.push(r.data);
15583         }
15584     },
15585     
15586     getAutoCreateNativeIOS : function()
15587     {
15588         var cfg = {
15589             cls: 'form-group' //input-group,
15590         };
15591         
15592         var combobox =  {
15593             tag: 'select',
15594             cls : 'roo-ios-select'
15595         };
15596         
15597         if (this.name) {
15598             combobox.name = this.name;
15599         }
15600         
15601         if (this.disabled) {
15602             combobox.disabled = true;
15603         }
15604         
15605         var settings = this;
15606         
15607         ['xs','sm','md','lg'].map(function(size){
15608             if (settings[size]) {
15609                 cfg.cls += ' col-' + size + '-' + settings[size];
15610             }
15611         });
15612         
15613         cfg.cn = combobox;
15614         
15615         return cfg;
15616         
15617     },
15618     
15619     initIOSView : function()
15620     {
15621         this.store.on('load', this.onIOSViewLoad, this);
15622         
15623         return;
15624     },
15625     
15626     onIOSViewLoad : function()
15627     {
15628         if(this.store.getCount() < 1){
15629             return;
15630         }
15631         
15632         this.clearIOSView();
15633         
15634         if(this.allowBlank) {
15635             
15636             var default_text = '-- SELECT --';
15637             
15638             if(this.placeholder.length){
15639                 default_text = this.placeholder;
15640             }
15641             
15642             if(this.emptyTitle.length){
15643                 default_text += ' - ' + this.emptyTitle + ' -';
15644             }
15645             
15646             var opt = this.inputEl().createChild({
15647                 tag: 'option',
15648                 value : 0,
15649                 html : default_text
15650             });
15651             
15652             var o = {};
15653             o[this.valueField] = 0;
15654             o[this.displayField] = default_text;
15655             
15656             this.ios_options.push({
15657                 data : o,
15658                 el : opt
15659             });
15660             
15661         }
15662         
15663         this.store.data.each(function(d, rowIndex){
15664             
15665             var html = '';
15666             
15667             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15668                 html = d.data[this.displayField];
15669             }
15670             
15671             var value = '';
15672             
15673             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15674                 value = d.data[this.valueField];
15675             }
15676             
15677             var option = {
15678                 tag: 'option',
15679                 value : value,
15680                 html : html
15681             };
15682             
15683             if(this.value == d.data[this.valueField]){
15684                 option['selected'] = true;
15685             }
15686             
15687             var opt = this.inputEl().createChild(option);
15688             
15689             this.ios_options.push({
15690                 data : d.data,
15691                 el : opt
15692             });
15693             
15694         }, this);
15695         
15696         this.inputEl().on('change', function(){
15697            this.fireEvent('select', this);
15698         }, this);
15699         
15700     },
15701     
15702     clearIOSView: function()
15703     {
15704         this.inputEl().dom.innerHTML = '';
15705         
15706         this.ios_options = [];
15707     },
15708     
15709     setIOSValue: function(v)
15710     {
15711         this.value = v;
15712         
15713         if(!this.ios_options){
15714             return;
15715         }
15716         
15717         Roo.each(this.ios_options, function(opts){
15718            
15719            opts.el.dom.removeAttribute('selected');
15720            
15721            if(opts.data[this.valueField] != v){
15722                return;
15723            }
15724            
15725            opts.el.dom.setAttribute('selected', true);
15726            
15727         }, this);
15728     }
15729
15730     /** 
15731     * @cfg {Boolean} grow 
15732     * @hide 
15733     */
15734     /** 
15735     * @cfg {Number} growMin 
15736     * @hide 
15737     */
15738     /** 
15739     * @cfg {Number} growMax 
15740     * @hide 
15741     */
15742     /**
15743      * @hide
15744      * @method autoSize
15745      */
15746 });
15747
15748 Roo.apply(Roo.bootstrap.ComboBox,  {
15749     
15750     header : {
15751         tag: 'div',
15752         cls: 'modal-header',
15753         cn: [
15754             {
15755                 tag: 'h4',
15756                 cls: 'modal-title'
15757             }
15758         ]
15759     },
15760     
15761     body : {
15762         tag: 'div',
15763         cls: 'modal-body',
15764         cn: [
15765             {
15766                 tag: 'ul',
15767                 cls: 'list-group'
15768             }
15769         ]
15770     },
15771     
15772     listItemRadio : {
15773         tag: 'li',
15774         cls: 'list-group-item',
15775         cn: [
15776             {
15777                 tag: 'span',
15778                 cls: 'roo-combobox-list-group-item-value'
15779             },
15780             {
15781                 tag: 'div',
15782                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15783                 cn: [
15784                     {
15785                         tag: 'input',
15786                         type: 'radio'
15787                     },
15788                     {
15789                         tag: 'label'
15790                     }
15791                 ]
15792             }
15793         ]
15794     },
15795     
15796     listItemCheckbox : {
15797         tag: 'li',
15798         cls: 'list-group-item',
15799         cn: [
15800             {
15801                 tag: 'span',
15802                 cls: 'roo-combobox-list-group-item-value'
15803             },
15804             {
15805                 tag: 'div',
15806                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15807                 cn: [
15808                     {
15809                         tag: 'input',
15810                         type: 'checkbox'
15811                     },
15812                     {
15813                         tag: 'label'
15814                     }
15815                 ]
15816             }
15817         ]
15818     },
15819     
15820     emptyResult : {
15821         tag: 'div',
15822         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15823     },
15824     
15825     footer : {
15826         tag: 'div',
15827         cls: 'modal-footer',
15828         cn: [
15829             {
15830                 tag: 'div',
15831                 cls: 'row',
15832                 cn: [
15833                     {
15834                         tag: 'div',
15835                         cls: 'col-xs-6 text-left',
15836                         cn: {
15837                             tag: 'button',
15838                             cls: 'btn btn-danger roo-touch-view-cancel',
15839                             html: 'Cancel'
15840                         }
15841                     },
15842                     {
15843                         tag: 'div',
15844                         cls: 'col-xs-6 text-right',
15845                         cn: {
15846                             tag: 'button',
15847                             cls: 'btn btn-success roo-touch-view-ok',
15848                             html: 'OK'
15849                         }
15850                     }
15851                 ]
15852             }
15853         ]
15854         
15855     }
15856 });
15857
15858 Roo.apply(Roo.bootstrap.ComboBox,  {
15859     
15860     touchViewTemplate : {
15861         tag: 'div',
15862         cls: 'modal fade roo-combobox-touch-view',
15863         cn: [
15864             {
15865                 tag: 'div',
15866                 cls: 'modal-dialog',
15867                 style : 'position:fixed', // we have to fix position....
15868                 cn: [
15869                     {
15870                         tag: 'div',
15871                         cls: 'modal-content',
15872                         cn: [
15873                             Roo.bootstrap.ComboBox.header,
15874                             Roo.bootstrap.ComboBox.body,
15875                             Roo.bootstrap.ComboBox.footer
15876                         ]
15877                     }
15878                 ]
15879             }
15880         ]
15881     }
15882 });/*
15883  * Based on:
15884  * Ext JS Library 1.1.1
15885  * Copyright(c) 2006-2007, Ext JS, LLC.
15886  *
15887  * Originally Released Under LGPL - original licence link has changed is not relivant.
15888  *
15889  * Fork - LGPL
15890  * <script type="text/javascript">
15891  */
15892
15893 /**
15894  * @class Roo.View
15895  * @extends Roo.util.Observable
15896  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15897  * This class also supports single and multi selection modes. <br>
15898  * Create a data model bound view:
15899  <pre><code>
15900  var store = new Roo.data.Store(...);
15901
15902  var view = new Roo.View({
15903     el : "my-element",
15904     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15905  
15906     singleSelect: true,
15907     selectedClass: "ydataview-selected",
15908     store: store
15909  });
15910
15911  // listen for node click?
15912  view.on("click", function(vw, index, node, e){
15913  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15914  });
15915
15916  // load XML data
15917  dataModel.load("foobar.xml");
15918  </code></pre>
15919  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15920  * <br><br>
15921  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15922  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15923  * 
15924  * Note: old style constructor is still suported (container, template, config)
15925  * 
15926  * @constructor
15927  * Create a new View
15928  * @param {Object} config The config object
15929  * 
15930  */
15931 Roo.View = function(config, depreciated_tpl, depreciated_config){
15932     
15933     this.parent = false;
15934     
15935     if (typeof(depreciated_tpl) == 'undefined') {
15936         // new way.. - universal constructor.
15937         Roo.apply(this, config);
15938         this.el  = Roo.get(this.el);
15939     } else {
15940         // old format..
15941         this.el  = Roo.get(config);
15942         this.tpl = depreciated_tpl;
15943         Roo.apply(this, depreciated_config);
15944     }
15945     this.wrapEl  = this.el.wrap().wrap();
15946     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15947     
15948     
15949     if(typeof(this.tpl) == "string"){
15950         this.tpl = new Roo.Template(this.tpl);
15951     } else {
15952         // support xtype ctors..
15953         this.tpl = new Roo.factory(this.tpl, Roo);
15954     }
15955     
15956     
15957     this.tpl.compile();
15958     
15959     /** @private */
15960     this.addEvents({
15961         /**
15962          * @event beforeclick
15963          * Fires before a click is processed. Returns false to cancel the default action.
15964          * @param {Roo.View} this
15965          * @param {Number} index The index of the target node
15966          * @param {HTMLElement} node The target node
15967          * @param {Roo.EventObject} e The raw event object
15968          */
15969             "beforeclick" : true,
15970         /**
15971          * @event click
15972          * Fires when a template node is clicked.
15973          * @param {Roo.View} this
15974          * @param {Number} index The index of the target node
15975          * @param {HTMLElement} node The target node
15976          * @param {Roo.EventObject} e The raw event object
15977          */
15978             "click" : true,
15979         /**
15980          * @event dblclick
15981          * Fires when a template node is double clicked.
15982          * @param {Roo.View} this
15983          * @param {Number} index The index of the target node
15984          * @param {HTMLElement} node The target node
15985          * @param {Roo.EventObject} e The raw event object
15986          */
15987             "dblclick" : true,
15988         /**
15989          * @event contextmenu
15990          * Fires when a template node is right clicked.
15991          * @param {Roo.View} this
15992          * @param {Number} index The index of the target node
15993          * @param {HTMLElement} node The target node
15994          * @param {Roo.EventObject} e The raw event object
15995          */
15996             "contextmenu" : true,
15997         /**
15998          * @event selectionchange
15999          * Fires when the selected nodes change.
16000          * @param {Roo.View} this
16001          * @param {Array} selections Array of the selected nodes
16002          */
16003             "selectionchange" : true,
16004     
16005         /**
16006          * @event beforeselect
16007          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16008          * @param {Roo.View} this
16009          * @param {HTMLElement} node The node to be selected
16010          * @param {Array} selections Array of currently selected nodes
16011          */
16012             "beforeselect" : true,
16013         /**
16014          * @event preparedata
16015          * Fires on every row to render, to allow you to change the data.
16016          * @param {Roo.View} this
16017          * @param {Object} data to be rendered (change this)
16018          */
16019           "preparedata" : true
16020           
16021           
16022         });
16023
16024
16025
16026     this.el.on({
16027         "click": this.onClick,
16028         "dblclick": this.onDblClick,
16029         "contextmenu": this.onContextMenu,
16030         scope:this
16031     });
16032
16033     this.selections = [];
16034     this.nodes = [];
16035     this.cmp = new Roo.CompositeElementLite([]);
16036     if(this.store){
16037         this.store = Roo.factory(this.store, Roo.data);
16038         this.setStore(this.store, true);
16039     }
16040     
16041     if ( this.footer && this.footer.xtype) {
16042            
16043          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16044         
16045         this.footer.dataSource = this.store;
16046         this.footer.container = fctr;
16047         this.footer = Roo.factory(this.footer, Roo);
16048         fctr.insertFirst(this.el);
16049         
16050         // this is a bit insane - as the paging toolbar seems to detach the el..
16051 //        dom.parentNode.parentNode.parentNode
16052          // they get detached?
16053     }
16054     
16055     
16056     Roo.View.superclass.constructor.call(this);
16057     
16058     
16059 };
16060
16061 Roo.extend(Roo.View, Roo.util.Observable, {
16062     
16063      /**
16064      * @cfg {Roo.data.Store} store Data store to load data from.
16065      */
16066     store : false,
16067     
16068     /**
16069      * @cfg {String|Roo.Element} el The container element.
16070      */
16071     el : '',
16072     
16073     /**
16074      * @cfg {String|Roo.Template} tpl The template used by this View 
16075      */
16076     tpl : false,
16077     /**
16078      * @cfg {String} dataName the named area of the template to use as the data area
16079      *                          Works with domtemplates roo-name="name"
16080      */
16081     dataName: false,
16082     /**
16083      * @cfg {String} selectedClass The css class to add to selected nodes
16084      */
16085     selectedClass : "x-view-selected",
16086      /**
16087      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16088      */
16089     emptyText : "",
16090     
16091     /**
16092      * @cfg {String} text to display on mask (default Loading)
16093      */
16094     mask : false,
16095     /**
16096      * @cfg {Boolean} multiSelect Allow multiple selection
16097      */
16098     multiSelect : false,
16099     /**
16100      * @cfg {Boolean} singleSelect Allow single selection
16101      */
16102     singleSelect:  false,
16103     
16104     /**
16105      * @cfg {Boolean} toggleSelect - selecting 
16106      */
16107     toggleSelect : false,
16108     
16109     /**
16110      * @cfg {Boolean} tickable - selecting 
16111      */
16112     tickable : false,
16113     
16114     /**
16115      * Returns the element this view is bound to.
16116      * @return {Roo.Element}
16117      */
16118     getEl : function(){
16119         return this.wrapEl;
16120     },
16121     
16122     
16123
16124     /**
16125      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16126      */
16127     refresh : function(){
16128         //Roo.log('refresh');
16129         var t = this.tpl;
16130         
16131         // if we are using something like 'domtemplate', then
16132         // the what gets used is:
16133         // t.applySubtemplate(NAME, data, wrapping data..)
16134         // the outer template then get' applied with
16135         //     the store 'extra data'
16136         // and the body get's added to the
16137         //      roo-name="data" node?
16138         //      <span class='roo-tpl-{name}'></span> ?????
16139         
16140         
16141         
16142         this.clearSelections();
16143         this.el.update("");
16144         var html = [];
16145         var records = this.store.getRange();
16146         if(records.length < 1) {
16147             
16148             // is this valid??  = should it render a template??
16149             
16150             this.el.update(this.emptyText);
16151             return;
16152         }
16153         var el = this.el;
16154         if (this.dataName) {
16155             this.el.update(t.apply(this.store.meta)); //????
16156             el = this.el.child('.roo-tpl-' + this.dataName);
16157         }
16158         
16159         for(var i = 0, len = records.length; i < len; i++){
16160             var data = this.prepareData(records[i].data, i, records[i]);
16161             this.fireEvent("preparedata", this, data, i, records[i]);
16162             
16163             var d = Roo.apply({}, data);
16164             
16165             if(this.tickable){
16166                 Roo.apply(d, {'roo-id' : Roo.id()});
16167                 
16168                 var _this = this;
16169             
16170                 Roo.each(this.parent.item, function(item){
16171                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16172                         return;
16173                     }
16174                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16175                 });
16176             }
16177             
16178             html[html.length] = Roo.util.Format.trim(
16179                 this.dataName ?
16180                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16181                     t.apply(d)
16182             );
16183         }
16184         
16185         
16186         
16187         el.update(html.join(""));
16188         this.nodes = el.dom.childNodes;
16189         this.updateIndexes(0);
16190     },
16191     
16192
16193     /**
16194      * Function to override to reformat the data that is sent to
16195      * the template for each node.
16196      * DEPRICATED - use the preparedata event handler.
16197      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16198      * a JSON object for an UpdateManager bound view).
16199      */
16200     prepareData : function(data, index, record)
16201     {
16202         this.fireEvent("preparedata", this, data, index, record);
16203         return data;
16204     },
16205
16206     onUpdate : function(ds, record){
16207         // Roo.log('on update');   
16208         this.clearSelections();
16209         var index = this.store.indexOf(record);
16210         var n = this.nodes[index];
16211         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16212         n.parentNode.removeChild(n);
16213         this.updateIndexes(index, index);
16214     },
16215
16216     
16217     
16218 // --------- FIXME     
16219     onAdd : function(ds, records, index)
16220     {
16221         //Roo.log(['on Add', ds, records, index] );        
16222         this.clearSelections();
16223         if(this.nodes.length == 0){
16224             this.refresh();
16225             return;
16226         }
16227         var n = this.nodes[index];
16228         for(var i = 0, len = records.length; i < len; i++){
16229             var d = this.prepareData(records[i].data, i, records[i]);
16230             if(n){
16231                 this.tpl.insertBefore(n, d);
16232             }else{
16233                 
16234                 this.tpl.append(this.el, d);
16235             }
16236         }
16237         this.updateIndexes(index);
16238     },
16239
16240     onRemove : function(ds, record, index){
16241        // Roo.log('onRemove');
16242         this.clearSelections();
16243         var el = this.dataName  ?
16244             this.el.child('.roo-tpl-' + this.dataName) :
16245             this.el; 
16246         
16247         el.dom.removeChild(this.nodes[index]);
16248         this.updateIndexes(index);
16249     },
16250
16251     /**
16252      * Refresh an individual node.
16253      * @param {Number} index
16254      */
16255     refreshNode : function(index){
16256         this.onUpdate(this.store, this.store.getAt(index));
16257     },
16258
16259     updateIndexes : function(startIndex, endIndex){
16260         var ns = this.nodes;
16261         startIndex = startIndex || 0;
16262         endIndex = endIndex || ns.length - 1;
16263         for(var i = startIndex; i <= endIndex; i++){
16264             ns[i].nodeIndex = i;
16265         }
16266     },
16267
16268     /**
16269      * Changes the data store this view uses and refresh the view.
16270      * @param {Store} store
16271      */
16272     setStore : function(store, initial){
16273         if(!initial && this.store){
16274             this.store.un("datachanged", this.refresh);
16275             this.store.un("add", this.onAdd);
16276             this.store.un("remove", this.onRemove);
16277             this.store.un("update", this.onUpdate);
16278             this.store.un("clear", this.refresh);
16279             this.store.un("beforeload", this.onBeforeLoad);
16280             this.store.un("load", this.onLoad);
16281             this.store.un("loadexception", this.onLoad);
16282         }
16283         if(store){
16284           
16285             store.on("datachanged", this.refresh, this);
16286             store.on("add", this.onAdd, this);
16287             store.on("remove", this.onRemove, this);
16288             store.on("update", this.onUpdate, this);
16289             store.on("clear", this.refresh, this);
16290             store.on("beforeload", this.onBeforeLoad, this);
16291             store.on("load", this.onLoad, this);
16292             store.on("loadexception", this.onLoad, this);
16293         }
16294         
16295         if(store){
16296             this.refresh();
16297         }
16298     },
16299     /**
16300      * onbeforeLoad - masks the loading area.
16301      *
16302      */
16303     onBeforeLoad : function(store,opts)
16304     {
16305          //Roo.log('onBeforeLoad');   
16306         if (!opts.add) {
16307             this.el.update("");
16308         }
16309         this.el.mask(this.mask ? this.mask : "Loading" ); 
16310     },
16311     onLoad : function ()
16312     {
16313         this.el.unmask();
16314     },
16315     
16316
16317     /**
16318      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16319      * @param {HTMLElement} node
16320      * @return {HTMLElement} The template node
16321      */
16322     findItemFromChild : function(node){
16323         var el = this.dataName  ?
16324             this.el.child('.roo-tpl-' + this.dataName,true) :
16325             this.el.dom; 
16326         
16327         if(!node || node.parentNode == el){
16328                     return node;
16329             }
16330             var p = node.parentNode;
16331             while(p && p != el){
16332             if(p.parentNode == el){
16333                 return p;
16334             }
16335             p = p.parentNode;
16336         }
16337             return null;
16338     },
16339
16340     /** @ignore */
16341     onClick : function(e){
16342         var item = this.findItemFromChild(e.getTarget());
16343         if(item){
16344             var index = this.indexOf(item);
16345             if(this.onItemClick(item, index, e) !== false){
16346                 this.fireEvent("click", this, index, item, e);
16347             }
16348         }else{
16349             this.clearSelections();
16350         }
16351     },
16352
16353     /** @ignore */
16354     onContextMenu : function(e){
16355         var item = this.findItemFromChild(e.getTarget());
16356         if(item){
16357             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16358         }
16359     },
16360
16361     /** @ignore */
16362     onDblClick : function(e){
16363         var item = this.findItemFromChild(e.getTarget());
16364         if(item){
16365             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16366         }
16367     },
16368
16369     onItemClick : function(item, index, e)
16370     {
16371         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16372             return false;
16373         }
16374         if (this.toggleSelect) {
16375             var m = this.isSelected(item) ? 'unselect' : 'select';
16376             //Roo.log(m);
16377             var _t = this;
16378             _t[m](item, true, false);
16379             return true;
16380         }
16381         if(this.multiSelect || this.singleSelect){
16382             if(this.multiSelect && e.shiftKey && this.lastSelection){
16383                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16384             }else{
16385                 this.select(item, this.multiSelect && e.ctrlKey);
16386                 this.lastSelection = item;
16387             }
16388             
16389             if(!this.tickable){
16390                 e.preventDefault();
16391             }
16392             
16393         }
16394         return true;
16395     },
16396
16397     /**
16398      * Get the number of selected nodes.
16399      * @return {Number}
16400      */
16401     getSelectionCount : function(){
16402         return this.selections.length;
16403     },
16404
16405     /**
16406      * Get the currently selected nodes.
16407      * @return {Array} An array of HTMLElements
16408      */
16409     getSelectedNodes : function(){
16410         return this.selections;
16411     },
16412
16413     /**
16414      * Get the indexes of the selected nodes.
16415      * @return {Array}
16416      */
16417     getSelectedIndexes : function(){
16418         var indexes = [], s = this.selections;
16419         for(var i = 0, len = s.length; i < len; i++){
16420             indexes.push(s[i].nodeIndex);
16421         }
16422         return indexes;
16423     },
16424
16425     /**
16426      * Clear all selections
16427      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16428      */
16429     clearSelections : function(suppressEvent){
16430         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16431             this.cmp.elements = this.selections;
16432             this.cmp.removeClass(this.selectedClass);
16433             this.selections = [];
16434             if(!suppressEvent){
16435                 this.fireEvent("selectionchange", this, this.selections);
16436             }
16437         }
16438     },
16439
16440     /**
16441      * Returns true if the passed node is selected
16442      * @param {HTMLElement/Number} node The node or node index
16443      * @return {Boolean}
16444      */
16445     isSelected : function(node){
16446         var s = this.selections;
16447         if(s.length < 1){
16448             return false;
16449         }
16450         node = this.getNode(node);
16451         return s.indexOf(node) !== -1;
16452     },
16453
16454     /**
16455      * Selects nodes.
16456      * @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
16457      * @param {Boolean} keepExisting (optional) true to keep existing selections
16458      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16459      */
16460     select : function(nodeInfo, keepExisting, suppressEvent){
16461         if(nodeInfo instanceof Array){
16462             if(!keepExisting){
16463                 this.clearSelections(true);
16464             }
16465             for(var i = 0, len = nodeInfo.length; i < len; i++){
16466                 this.select(nodeInfo[i], true, true);
16467             }
16468             return;
16469         } 
16470         var node = this.getNode(nodeInfo);
16471         if(!node || this.isSelected(node)){
16472             return; // already selected.
16473         }
16474         if(!keepExisting){
16475             this.clearSelections(true);
16476         }
16477         
16478         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16479             Roo.fly(node).addClass(this.selectedClass);
16480             this.selections.push(node);
16481             if(!suppressEvent){
16482                 this.fireEvent("selectionchange", this, this.selections);
16483             }
16484         }
16485         
16486         
16487     },
16488       /**
16489      * Unselects nodes.
16490      * @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
16491      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16492      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16493      */
16494     unselect : function(nodeInfo, keepExisting, suppressEvent)
16495     {
16496         if(nodeInfo instanceof Array){
16497             Roo.each(this.selections, function(s) {
16498                 this.unselect(s, nodeInfo);
16499             }, this);
16500             return;
16501         }
16502         var node = this.getNode(nodeInfo);
16503         if(!node || !this.isSelected(node)){
16504             //Roo.log("not selected");
16505             return; // not selected.
16506         }
16507         // fireevent???
16508         var ns = [];
16509         Roo.each(this.selections, function(s) {
16510             if (s == node ) {
16511                 Roo.fly(node).removeClass(this.selectedClass);
16512
16513                 return;
16514             }
16515             ns.push(s);
16516         },this);
16517         
16518         this.selections= ns;
16519         this.fireEvent("selectionchange", this, this.selections);
16520     },
16521
16522     /**
16523      * Gets a template node.
16524      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16525      * @return {HTMLElement} The node or null if it wasn't found
16526      */
16527     getNode : function(nodeInfo){
16528         if(typeof nodeInfo == "string"){
16529             return document.getElementById(nodeInfo);
16530         }else if(typeof nodeInfo == "number"){
16531             return this.nodes[nodeInfo];
16532         }
16533         return nodeInfo;
16534     },
16535
16536     /**
16537      * Gets a range template nodes.
16538      * @param {Number} startIndex
16539      * @param {Number} endIndex
16540      * @return {Array} An array of nodes
16541      */
16542     getNodes : function(start, end){
16543         var ns = this.nodes;
16544         start = start || 0;
16545         end = typeof end == "undefined" ? ns.length - 1 : end;
16546         var nodes = [];
16547         if(start <= end){
16548             for(var i = start; i <= end; i++){
16549                 nodes.push(ns[i]);
16550             }
16551         } else{
16552             for(var i = start; i >= end; i--){
16553                 nodes.push(ns[i]);
16554             }
16555         }
16556         return nodes;
16557     },
16558
16559     /**
16560      * Finds the index of the passed node
16561      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16562      * @return {Number} The index of the node or -1
16563      */
16564     indexOf : function(node){
16565         node = this.getNode(node);
16566         if(typeof node.nodeIndex == "number"){
16567             return node.nodeIndex;
16568         }
16569         var ns = this.nodes;
16570         for(var i = 0, len = ns.length; i < len; i++){
16571             if(ns[i] == node){
16572                 return i;
16573             }
16574         }
16575         return -1;
16576     }
16577 });
16578 /*
16579  * - LGPL
16580  *
16581  * based on jquery fullcalendar
16582  * 
16583  */
16584
16585 Roo.bootstrap = Roo.bootstrap || {};
16586 /**
16587  * @class Roo.bootstrap.Calendar
16588  * @extends Roo.bootstrap.Component
16589  * Bootstrap Calendar class
16590  * @cfg {Boolean} loadMask (true|false) default false
16591  * @cfg {Object} header generate the user specific header of the calendar, default false
16592
16593  * @constructor
16594  * Create a new Container
16595  * @param {Object} config The config object
16596  */
16597
16598
16599
16600 Roo.bootstrap.Calendar = function(config){
16601     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16602      this.addEvents({
16603         /**
16604              * @event select
16605              * Fires when a date is selected
16606              * @param {DatePicker} this
16607              * @param {Date} date The selected date
16608              */
16609         'select': true,
16610         /**
16611              * @event monthchange
16612              * Fires when the displayed month changes 
16613              * @param {DatePicker} this
16614              * @param {Date} date The selected month
16615              */
16616         'monthchange': true,
16617         /**
16618              * @event evententer
16619              * Fires when mouse over an event
16620              * @param {Calendar} this
16621              * @param {event} Event
16622              */
16623         'evententer': true,
16624         /**
16625              * @event eventleave
16626              * Fires when the mouse leaves an
16627              * @param {Calendar} this
16628              * @param {event}
16629              */
16630         'eventleave': true,
16631         /**
16632              * @event eventclick
16633              * Fires when the mouse click an
16634              * @param {Calendar} this
16635              * @param {event}
16636              */
16637         'eventclick': true
16638         
16639     });
16640
16641 };
16642
16643 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16644     
16645      /**
16646      * @cfg {Number} startDay
16647      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16648      */
16649     startDay : 0,
16650     
16651     loadMask : false,
16652     
16653     header : false,
16654       
16655     getAutoCreate : function(){
16656         
16657         
16658         var fc_button = function(name, corner, style, content ) {
16659             return Roo.apply({},{
16660                 tag : 'span',
16661                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16662                          (corner.length ?
16663                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16664                             ''
16665                         ),
16666                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16667                 unselectable: 'on'
16668             });
16669         };
16670         
16671         var header = {};
16672         
16673         if(!this.header){
16674             header = {
16675                 tag : 'table',
16676                 cls : 'fc-header',
16677                 style : 'width:100%',
16678                 cn : [
16679                     {
16680                         tag: 'tr',
16681                         cn : [
16682                             {
16683                                 tag : 'td',
16684                                 cls : 'fc-header-left',
16685                                 cn : [
16686                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16687                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16688                                     { tag: 'span', cls: 'fc-header-space' },
16689                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16690
16691
16692                                 ]
16693                             },
16694
16695                             {
16696                                 tag : 'td',
16697                                 cls : 'fc-header-center',
16698                                 cn : [
16699                                     {
16700                                         tag: 'span',
16701                                         cls: 'fc-header-title',
16702                                         cn : {
16703                                             tag: 'H2',
16704                                             html : 'month / year'
16705                                         }
16706                                     }
16707
16708                                 ]
16709                             },
16710                             {
16711                                 tag : 'td',
16712                                 cls : 'fc-header-right',
16713                                 cn : [
16714                               /*      fc_button('month', 'left', '', 'month' ),
16715                                     fc_button('week', '', '', 'week' ),
16716                                     fc_button('day', 'right', '', 'day' )
16717                                 */    
16718
16719                                 ]
16720                             }
16721
16722                         ]
16723                     }
16724                 ]
16725             };
16726         }
16727         
16728         header = this.header;
16729         
16730        
16731         var cal_heads = function() {
16732             var ret = [];
16733             // fixme - handle this.
16734             
16735             for (var i =0; i < Date.dayNames.length; i++) {
16736                 var d = Date.dayNames[i];
16737                 ret.push({
16738                     tag: 'th',
16739                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16740                     html : d.substring(0,3)
16741                 });
16742                 
16743             }
16744             ret[0].cls += ' fc-first';
16745             ret[6].cls += ' fc-last';
16746             return ret;
16747         };
16748         var cal_cell = function(n) {
16749             return  {
16750                 tag: 'td',
16751                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16752                 cn : [
16753                     {
16754                         cn : [
16755                             {
16756                                 cls: 'fc-day-number',
16757                                 html: 'D'
16758                             },
16759                             {
16760                                 cls: 'fc-day-content',
16761                              
16762                                 cn : [
16763                                      {
16764                                         style: 'position: relative;' // height: 17px;
16765                                     }
16766                                 ]
16767                             }
16768                             
16769                             
16770                         ]
16771                     }
16772                 ]
16773                 
16774             }
16775         };
16776         var cal_rows = function() {
16777             
16778             var ret = [];
16779             for (var r = 0; r < 6; r++) {
16780                 var row= {
16781                     tag : 'tr',
16782                     cls : 'fc-week',
16783                     cn : []
16784                 };
16785                 
16786                 for (var i =0; i < Date.dayNames.length; i++) {
16787                     var d = Date.dayNames[i];
16788                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16789
16790                 }
16791                 row.cn[0].cls+=' fc-first';
16792                 row.cn[0].cn[0].style = 'min-height:90px';
16793                 row.cn[6].cls+=' fc-last';
16794                 ret.push(row);
16795                 
16796             }
16797             ret[0].cls += ' fc-first';
16798             ret[4].cls += ' fc-prev-last';
16799             ret[5].cls += ' fc-last';
16800             return ret;
16801             
16802         };
16803         
16804         var cal_table = {
16805             tag: 'table',
16806             cls: 'fc-border-separate',
16807             style : 'width:100%',
16808             cellspacing  : 0,
16809             cn : [
16810                 { 
16811                     tag: 'thead',
16812                     cn : [
16813                         { 
16814                             tag: 'tr',
16815                             cls : 'fc-first fc-last',
16816                             cn : cal_heads()
16817                         }
16818                     ]
16819                 },
16820                 { 
16821                     tag: 'tbody',
16822                     cn : cal_rows()
16823                 }
16824                   
16825             ]
16826         };
16827          
16828          var cfg = {
16829             cls : 'fc fc-ltr',
16830             cn : [
16831                 header,
16832                 {
16833                     cls : 'fc-content',
16834                     style : "position: relative;",
16835                     cn : [
16836                         {
16837                             cls : 'fc-view fc-view-month fc-grid',
16838                             style : 'position: relative',
16839                             unselectable : 'on',
16840                             cn : [
16841                                 {
16842                                     cls : 'fc-event-container',
16843                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16844                                 },
16845                                 cal_table
16846                             ]
16847                         }
16848                     ]
16849     
16850                 }
16851            ] 
16852             
16853         };
16854         
16855          
16856         
16857         return cfg;
16858     },
16859     
16860     
16861     initEvents : function()
16862     {
16863         if(!this.store){
16864             throw "can not find store for calendar";
16865         }
16866         
16867         var mark = {
16868             tag: "div",
16869             cls:"x-dlg-mask",
16870             style: "text-align:center",
16871             cn: [
16872                 {
16873                     tag: "div",
16874                     style: "background-color:white;width:50%;margin:250 auto",
16875                     cn: [
16876                         {
16877                             tag: "img",
16878                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16879                         },
16880                         {
16881                             tag: "span",
16882                             html: "Loading"
16883                         }
16884                         
16885                     ]
16886                 }
16887             ]
16888         };
16889         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16890         
16891         var size = this.el.select('.fc-content', true).first().getSize();
16892         this.maskEl.setSize(size.width, size.height);
16893         this.maskEl.enableDisplayMode("block");
16894         if(!this.loadMask){
16895             this.maskEl.hide();
16896         }
16897         
16898         this.store = Roo.factory(this.store, Roo.data);
16899         this.store.on('load', this.onLoad, this);
16900         this.store.on('beforeload', this.onBeforeLoad, this);
16901         
16902         this.resize();
16903         
16904         this.cells = this.el.select('.fc-day',true);
16905         //Roo.log(this.cells);
16906         this.textNodes = this.el.query('.fc-day-number');
16907         this.cells.addClassOnOver('fc-state-hover');
16908         
16909         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16910         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16911         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16912         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16913         
16914         this.on('monthchange', this.onMonthChange, this);
16915         
16916         this.update(new Date().clearTime());
16917     },
16918     
16919     resize : function() {
16920         var sz  = this.el.getSize();
16921         
16922         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16923         this.el.select('.fc-day-content div',true).setHeight(34);
16924     },
16925     
16926     
16927     // private
16928     showPrevMonth : function(e){
16929         this.update(this.activeDate.add("mo", -1));
16930     },
16931     showToday : function(e){
16932         this.update(new Date().clearTime());
16933     },
16934     // private
16935     showNextMonth : function(e){
16936         this.update(this.activeDate.add("mo", 1));
16937     },
16938
16939     // private
16940     showPrevYear : function(){
16941         this.update(this.activeDate.add("y", -1));
16942     },
16943
16944     // private
16945     showNextYear : function(){
16946         this.update(this.activeDate.add("y", 1));
16947     },
16948
16949     
16950    // private
16951     update : function(date)
16952     {
16953         var vd = this.activeDate;
16954         this.activeDate = date;
16955 //        if(vd && this.el){
16956 //            var t = date.getTime();
16957 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16958 //                Roo.log('using add remove');
16959 //                
16960 //                this.fireEvent('monthchange', this, date);
16961 //                
16962 //                this.cells.removeClass("fc-state-highlight");
16963 //                this.cells.each(function(c){
16964 //                   if(c.dateValue == t){
16965 //                       c.addClass("fc-state-highlight");
16966 //                       setTimeout(function(){
16967 //                            try{c.dom.firstChild.focus();}catch(e){}
16968 //                       }, 50);
16969 //                       return false;
16970 //                   }
16971 //                   return true;
16972 //                });
16973 //                return;
16974 //            }
16975 //        }
16976         
16977         var days = date.getDaysInMonth();
16978         
16979         var firstOfMonth = date.getFirstDateOfMonth();
16980         var startingPos = firstOfMonth.getDay()-this.startDay;
16981         
16982         if(startingPos < this.startDay){
16983             startingPos += 7;
16984         }
16985         
16986         var pm = date.add(Date.MONTH, -1);
16987         var prevStart = pm.getDaysInMonth()-startingPos;
16988 //        
16989         this.cells = this.el.select('.fc-day',true);
16990         this.textNodes = this.el.query('.fc-day-number');
16991         this.cells.addClassOnOver('fc-state-hover');
16992         
16993         var cells = this.cells.elements;
16994         var textEls = this.textNodes;
16995         
16996         Roo.each(cells, function(cell){
16997             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16998         });
16999         
17000         days += startingPos;
17001
17002         // convert everything to numbers so it's fast
17003         var day = 86400000;
17004         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17005         //Roo.log(d);
17006         //Roo.log(pm);
17007         //Roo.log(prevStart);
17008         
17009         var today = new Date().clearTime().getTime();
17010         var sel = date.clearTime().getTime();
17011         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17012         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17013         var ddMatch = this.disabledDatesRE;
17014         var ddText = this.disabledDatesText;
17015         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17016         var ddaysText = this.disabledDaysText;
17017         var format = this.format;
17018         
17019         var setCellClass = function(cal, cell){
17020             cell.row = 0;
17021             cell.events = [];
17022             cell.more = [];
17023             //Roo.log('set Cell Class');
17024             cell.title = "";
17025             var t = d.getTime();
17026             
17027             //Roo.log(d);
17028             
17029             cell.dateValue = t;
17030             if(t == today){
17031                 cell.className += " fc-today";
17032                 cell.className += " fc-state-highlight";
17033                 cell.title = cal.todayText;
17034             }
17035             if(t == sel){
17036                 // disable highlight in other month..
17037                 //cell.className += " fc-state-highlight";
17038                 
17039             }
17040             // disabling
17041             if(t < min) {
17042                 cell.className = " fc-state-disabled";
17043                 cell.title = cal.minText;
17044                 return;
17045             }
17046             if(t > max) {
17047                 cell.className = " fc-state-disabled";
17048                 cell.title = cal.maxText;
17049                 return;
17050             }
17051             if(ddays){
17052                 if(ddays.indexOf(d.getDay()) != -1){
17053                     cell.title = ddaysText;
17054                     cell.className = " fc-state-disabled";
17055                 }
17056             }
17057             if(ddMatch && format){
17058                 var fvalue = d.dateFormat(format);
17059                 if(ddMatch.test(fvalue)){
17060                     cell.title = ddText.replace("%0", fvalue);
17061                     cell.className = " fc-state-disabled";
17062                 }
17063             }
17064             
17065             if (!cell.initialClassName) {
17066                 cell.initialClassName = cell.dom.className;
17067             }
17068             
17069             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17070         };
17071
17072         var i = 0;
17073         
17074         for(; i < startingPos; i++) {
17075             textEls[i].innerHTML = (++prevStart);
17076             d.setDate(d.getDate()+1);
17077             
17078             cells[i].className = "fc-past fc-other-month";
17079             setCellClass(this, cells[i]);
17080         }
17081         
17082         var intDay = 0;
17083         
17084         for(; i < days; i++){
17085             intDay = i - startingPos + 1;
17086             textEls[i].innerHTML = (intDay);
17087             d.setDate(d.getDate()+1);
17088             
17089             cells[i].className = ''; // "x-date-active";
17090             setCellClass(this, cells[i]);
17091         }
17092         var extraDays = 0;
17093         
17094         for(; i < 42; i++) {
17095             textEls[i].innerHTML = (++extraDays);
17096             d.setDate(d.getDate()+1);
17097             
17098             cells[i].className = "fc-future fc-other-month";
17099             setCellClass(this, cells[i]);
17100         }
17101         
17102         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17103         
17104         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17105         
17106         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17107         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17108         
17109         if(totalRows != 6){
17110             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17111             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17112         }
17113         
17114         this.fireEvent('monthchange', this, date);
17115         
17116         
17117         /*
17118         if(!this.internalRender){
17119             var main = this.el.dom.firstChild;
17120             var w = main.offsetWidth;
17121             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17122             Roo.fly(main).setWidth(w);
17123             this.internalRender = true;
17124             // opera does not respect the auto grow header center column
17125             // then, after it gets a width opera refuses to recalculate
17126             // without a second pass
17127             if(Roo.isOpera && !this.secondPass){
17128                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17129                 this.secondPass = true;
17130                 this.update.defer(10, this, [date]);
17131             }
17132         }
17133         */
17134         
17135     },
17136     
17137     findCell : function(dt) {
17138         dt = dt.clearTime().getTime();
17139         var ret = false;
17140         this.cells.each(function(c){
17141             //Roo.log("check " +c.dateValue + '?=' + dt);
17142             if(c.dateValue == dt){
17143                 ret = c;
17144                 return false;
17145             }
17146             return true;
17147         });
17148         
17149         return ret;
17150     },
17151     
17152     findCells : function(ev) {
17153         var s = ev.start.clone().clearTime().getTime();
17154        // Roo.log(s);
17155         var e= ev.end.clone().clearTime().getTime();
17156        // Roo.log(e);
17157         var ret = [];
17158         this.cells.each(function(c){
17159              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17160             
17161             if(c.dateValue > e){
17162                 return ;
17163             }
17164             if(c.dateValue < s){
17165                 return ;
17166             }
17167             ret.push(c);
17168         });
17169         
17170         return ret;    
17171     },
17172     
17173 //    findBestRow: function(cells)
17174 //    {
17175 //        var ret = 0;
17176 //        
17177 //        for (var i =0 ; i < cells.length;i++) {
17178 //            ret  = Math.max(cells[i].rows || 0,ret);
17179 //        }
17180 //        return ret;
17181 //        
17182 //    },
17183     
17184     
17185     addItem : function(ev)
17186     {
17187         // look for vertical location slot in
17188         var cells = this.findCells(ev);
17189         
17190 //        ev.row = this.findBestRow(cells);
17191         
17192         // work out the location.
17193         
17194         var crow = false;
17195         var rows = [];
17196         for(var i =0; i < cells.length; i++) {
17197             
17198             cells[i].row = cells[0].row;
17199             
17200             if(i == 0){
17201                 cells[i].row = cells[i].row + 1;
17202             }
17203             
17204             if (!crow) {
17205                 crow = {
17206                     start : cells[i],
17207                     end :  cells[i]
17208                 };
17209                 continue;
17210             }
17211             if (crow.start.getY() == cells[i].getY()) {
17212                 // on same row.
17213                 crow.end = cells[i];
17214                 continue;
17215             }
17216             // different row.
17217             rows.push(crow);
17218             crow = {
17219                 start: cells[i],
17220                 end : cells[i]
17221             };
17222             
17223         }
17224         
17225         rows.push(crow);
17226         ev.els = [];
17227         ev.rows = rows;
17228         ev.cells = cells;
17229         
17230         cells[0].events.push(ev);
17231         
17232         this.calevents.push(ev);
17233     },
17234     
17235     clearEvents: function() {
17236         
17237         if(!this.calevents){
17238             return;
17239         }
17240         
17241         Roo.each(this.cells.elements, function(c){
17242             c.row = 0;
17243             c.events = [];
17244             c.more = [];
17245         });
17246         
17247         Roo.each(this.calevents, function(e) {
17248             Roo.each(e.els, function(el) {
17249                 el.un('mouseenter' ,this.onEventEnter, this);
17250                 el.un('mouseleave' ,this.onEventLeave, this);
17251                 el.remove();
17252             },this);
17253         },this);
17254         
17255         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17256             e.remove();
17257         });
17258         
17259     },
17260     
17261     renderEvents: function()
17262     {   
17263         var _this = this;
17264         
17265         this.cells.each(function(c) {
17266             
17267             if(c.row < 5){
17268                 return;
17269             }
17270             
17271             var ev = c.events;
17272             
17273             var r = 4;
17274             if(c.row != c.events.length){
17275                 r = 4 - (4 - (c.row - c.events.length));
17276             }
17277             
17278             c.events = ev.slice(0, r);
17279             c.more = ev.slice(r);
17280             
17281             if(c.more.length && c.more.length == 1){
17282                 c.events.push(c.more.pop());
17283             }
17284             
17285             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17286             
17287         });
17288             
17289         this.cells.each(function(c) {
17290             
17291             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17292             
17293             
17294             for (var e = 0; e < c.events.length; e++){
17295                 var ev = c.events[e];
17296                 var rows = ev.rows;
17297                 
17298                 for(var i = 0; i < rows.length; i++) {
17299                 
17300                     // how many rows should it span..
17301
17302                     var  cfg = {
17303                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17304                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17305
17306                         unselectable : "on",
17307                         cn : [
17308                             {
17309                                 cls: 'fc-event-inner',
17310                                 cn : [
17311     //                                {
17312     //                                  tag:'span',
17313     //                                  cls: 'fc-event-time',
17314     //                                  html : cells.length > 1 ? '' : ev.time
17315     //                                },
17316                                     {
17317                                       tag:'span',
17318                                       cls: 'fc-event-title',
17319                                       html : String.format('{0}', ev.title)
17320                                     }
17321
17322
17323                                 ]
17324                             },
17325                             {
17326                                 cls: 'ui-resizable-handle ui-resizable-e',
17327                                 html : '&nbsp;&nbsp;&nbsp'
17328                             }
17329
17330                         ]
17331                     };
17332
17333                     if (i == 0) {
17334                         cfg.cls += ' fc-event-start';
17335                     }
17336                     if ((i+1) == rows.length) {
17337                         cfg.cls += ' fc-event-end';
17338                     }
17339
17340                     var ctr = _this.el.select('.fc-event-container',true).first();
17341                     var cg = ctr.createChild(cfg);
17342
17343                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17344                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17345
17346                     var r = (c.more.length) ? 1 : 0;
17347                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17348                     cg.setWidth(ebox.right - sbox.x -2);
17349
17350                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17351                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17352                     cg.on('click', _this.onEventClick, _this, ev);
17353
17354                     ev.els.push(cg);
17355                     
17356                 }
17357                 
17358             }
17359             
17360             
17361             if(c.more.length){
17362                 var  cfg = {
17363                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17364                     style : 'position: absolute',
17365                     unselectable : "on",
17366                     cn : [
17367                         {
17368                             cls: 'fc-event-inner',
17369                             cn : [
17370                                 {
17371                                   tag:'span',
17372                                   cls: 'fc-event-title',
17373                                   html : 'More'
17374                                 }
17375
17376
17377                             ]
17378                         },
17379                         {
17380                             cls: 'ui-resizable-handle ui-resizable-e',
17381                             html : '&nbsp;&nbsp;&nbsp'
17382                         }
17383
17384                     ]
17385                 };
17386
17387                 var ctr = _this.el.select('.fc-event-container',true).first();
17388                 var cg = ctr.createChild(cfg);
17389
17390                 var sbox = c.select('.fc-day-content',true).first().getBox();
17391                 var ebox = c.select('.fc-day-content',true).first().getBox();
17392                 //Roo.log(cg);
17393                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17394                 cg.setWidth(ebox.right - sbox.x -2);
17395
17396                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17397                 
17398             }
17399             
17400         });
17401         
17402         
17403         
17404     },
17405     
17406     onEventEnter: function (e, el,event,d) {
17407         this.fireEvent('evententer', this, el, event);
17408     },
17409     
17410     onEventLeave: function (e, el,event,d) {
17411         this.fireEvent('eventleave', this, el, event);
17412     },
17413     
17414     onEventClick: function (e, el,event,d) {
17415         this.fireEvent('eventclick', this, el, event);
17416     },
17417     
17418     onMonthChange: function () {
17419         this.store.load();
17420     },
17421     
17422     onMoreEventClick: function(e, el, more)
17423     {
17424         var _this = this;
17425         
17426         this.calpopover.placement = 'right';
17427         this.calpopover.setTitle('More');
17428         
17429         this.calpopover.setContent('');
17430         
17431         var ctr = this.calpopover.el.select('.popover-content', true).first();
17432         
17433         Roo.each(more, function(m){
17434             var cfg = {
17435                 cls : 'fc-event-hori fc-event-draggable',
17436                 html : m.title
17437             };
17438             var cg = ctr.createChild(cfg);
17439             
17440             cg.on('click', _this.onEventClick, _this, m);
17441         });
17442         
17443         this.calpopover.show(el);
17444         
17445         
17446     },
17447     
17448     onLoad: function () 
17449     {   
17450         this.calevents = [];
17451         var cal = this;
17452         
17453         if(this.store.getCount() > 0){
17454             this.store.data.each(function(d){
17455                cal.addItem({
17456                     id : d.data.id,
17457                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17458                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17459                     time : d.data.start_time,
17460                     title : d.data.title,
17461                     description : d.data.description,
17462                     venue : d.data.venue
17463                 });
17464             });
17465         }
17466         
17467         this.renderEvents();
17468         
17469         if(this.calevents.length && this.loadMask){
17470             this.maskEl.hide();
17471         }
17472     },
17473     
17474     onBeforeLoad: function()
17475     {
17476         this.clearEvents();
17477         if(this.loadMask){
17478             this.maskEl.show();
17479         }
17480     }
17481 });
17482
17483  
17484  /*
17485  * - LGPL
17486  *
17487  * element
17488  * 
17489  */
17490
17491 /**
17492  * @class Roo.bootstrap.Popover
17493  * @extends Roo.bootstrap.Component
17494  * Bootstrap Popover class
17495  * @cfg {String} html contents of the popover   (or false to use children..)
17496  * @cfg {String} title of popover (or false to hide)
17497  * @cfg {String} placement how it is placed
17498  * @cfg {String} trigger click || hover (or false to trigger manually)
17499  * @cfg {String} over what (parent or false to trigger manually.)
17500  * @cfg {Number} delay - delay before showing
17501  
17502  * @constructor
17503  * Create a new Popover
17504  * @param {Object} config The config object
17505  */
17506
17507 Roo.bootstrap.Popover = function(config){
17508     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17509     
17510     this.addEvents({
17511         // raw events
17512          /**
17513          * @event show
17514          * After the popover show
17515          * 
17516          * @param {Roo.bootstrap.Popover} this
17517          */
17518         "show" : true,
17519         /**
17520          * @event hide
17521          * After the popover hide
17522          * 
17523          * @param {Roo.bootstrap.Popover} this
17524          */
17525         "hide" : true
17526     });
17527 };
17528
17529 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17530     
17531     title: 'Fill in a title',
17532     html: false,
17533     
17534     placement : 'right',
17535     trigger : 'hover', // hover
17536     
17537     delay : 0,
17538     
17539     over: 'parent',
17540     
17541     can_build_overlaid : false,
17542     
17543     getChildContainer : function()
17544     {
17545         return this.el.select('.popover-content',true).first();
17546     },
17547     
17548     getAutoCreate : function(){
17549          
17550         var cfg = {
17551            cls : 'popover roo-dynamic',
17552            style: 'display:block',
17553            cn : [
17554                 {
17555                     cls : 'arrow'
17556                 },
17557                 {
17558                     cls : 'popover-inner',
17559                     cn : [
17560                         {
17561                             tag: 'h3',
17562                             cls: 'popover-title',
17563                             html : this.title
17564                         },
17565                         {
17566                             cls : 'popover-content',
17567                             html : this.html
17568                         }
17569                     ]
17570                     
17571                 }
17572            ]
17573         };
17574         
17575         return cfg;
17576     },
17577     setTitle: function(str)
17578     {
17579         this.title = str;
17580         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17581     },
17582     setContent: function(str)
17583     {
17584         this.html = str;
17585         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17586     },
17587     // as it get's added to the bottom of the page.
17588     onRender : function(ct, position)
17589     {
17590         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17591         if(!this.el){
17592             var cfg = Roo.apply({},  this.getAutoCreate());
17593             cfg.id = Roo.id();
17594             
17595             if (this.cls) {
17596                 cfg.cls += ' ' + this.cls;
17597             }
17598             if (this.style) {
17599                 cfg.style = this.style;
17600             }
17601             //Roo.log("adding to ");
17602             this.el = Roo.get(document.body).createChild(cfg, position);
17603 //            Roo.log(this.el);
17604         }
17605         this.initEvents();
17606     },
17607     
17608     initEvents : function()
17609     {
17610         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17611         this.el.enableDisplayMode('block');
17612         this.el.hide();
17613         if (this.over === false) {
17614             return; 
17615         }
17616         if (this.triggers === false) {
17617             return;
17618         }
17619         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17620         var triggers = this.trigger ? this.trigger.split(' ') : [];
17621         Roo.each(triggers, function(trigger) {
17622         
17623             if (trigger == 'click') {
17624                 on_el.on('click', this.toggle, this);
17625             } else if (trigger != 'manual') {
17626                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17627                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17628       
17629                 on_el.on(eventIn  ,this.enter, this);
17630                 on_el.on(eventOut, this.leave, this);
17631             }
17632         }, this);
17633         
17634     },
17635     
17636     
17637     // private
17638     timeout : null,
17639     hoverState : null,
17640     
17641     toggle : function () {
17642         this.hoverState == 'in' ? this.leave() : this.enter();
17643     },
17644     
17645     enter : function () {
17646         
17647         clearTimeout(this.timeout);
17648     
17649         this.hoverState = 'in';
17650     
17651         if (!this.delay || !this.delay.show) {
17652             this.show();
17653             return;
17654         }
17655         var _t = this;
17656         this.timeout = setTimeout(function () {
17657             if (_t.hoverState == 'in') {
17658                 _t.show();
17659             }
17660         }, this.delay.show)
17661     },
17662     
17663     leave : function() {
17664         clearTimeout(this.timeout);
17665     
17666         this.hoverState = 'out';
17667     
17668         if (!this.delay || !this.delay.hide) {
17669             this.hide();
17670             return;
17671         }
17672         var _t = this;
17673         this.timeout = setTimeout(function () {
17674             if (_t.hoverState == 'out') {
17675                 _t.hide();
17676             }
17677         }, this.delay.hide)
17678     },
17679     
17680     show : function (on_el)
17681     {
17682         if (!on_el) {
17683             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17684         }
17685         
17686         // set content.
17687         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17688         if (this.html !== false) {
17689             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17690         }
17691         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17692         if (!this.title.length) {
17693             this.el.select('.popover-title',true).hide();
17694         }
17695         
17696         var placement = typeof this.placement == 'function' ?
17697             this.placement.call(this, this.el, on_el) :
17698             this.placement;
17699             
17700         var autoToken = /\s?auto?\s?/i;
17701         var autoPlace = autoToken.test(placement);
17702         if (autoPlace) {
17703             placement = placement.replace(autoToken, '') || 'top';
17704         }
17705         
17706         //this.el.detach()
17707         //this.el.setXY([0,0]);
17708         this.el.show();
17709         this.el.dom.style.display='block';
17710         this.el.addClass(placement);
17711         
17712         //this.el.appendTo(on_el);
17713         
17714         var p = this.getPosition();
17715         var box = this.el.getBox();
17716         
17717         if (autoPlace) {
17718             // fixme..
17719         }
17720         var align = Roo.bootstrap.Popover.alignment[placement];
17721         
17722 //        Roo.log(align);
17723         this.el.alignTo(on_el, align[0],align[1]);
17724         //var arrow = this.el.select('.arrow',true).first();
17725         //arrow.set(align[2], 
17726         
17727         this.el.addClass('in');
17728         
17729         
17730         if (this.el.hasClass('fade')) {
17731             // fade it?
17732         }
17733         
17734         this.hoverState = 'in';
17735         
17736         this.fireEvent('show', this);
17737         
17738     },
17739     hide : function()
17740     {
17741         this.el.setXY([0,0]);
17742         this.el.removeClass('in');
17743         this.el.hide();
17744         this.hoverState = null;
17745         
17746         this.fireEvent('hide', this);
17747     }
17748     
17749 });
17750
17751 Roo.bootstrap.Popover.alignment = {
17752     'left' : ['r-l', [-10,0], 'right'],
17753     'right' : ['l-r', [10,0], 'left'],
17754     'bottom' : ['t-b', [0,10], 'top'],
17755     'top' : [ 'b-t', [0,-10], 'bottom']
17756 };
17757
17758  /*
17759  * - LGPL
17760  *
17761  * Progress
17762  * 
17763  */
17764
17765 /**
17766  * @class Roo.bootstrap.Progress
17767  * @extends Roo.bootstrap.Component
17768  * Bootstrap Progress class
17769  * @cfg {Boolean} striped striped of the progress bar
17770  * @cfg {Boolean} active animated of the progress bar
17771  * 
17772  * 
17773  * @constructor
17774  * Create a new Progress
17775  * @param {Object} config The config object
17776  */
17777
17778 Roo.bootstrap.Progress = function(config){
17779     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17780 };
17781
17782 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17783     
17784     striped : false,
17785     active: false,
17786     
17787     getAutoCreate : function(){
17788         var cfg = {
17789             tag: 'div',
17790             cls: 'progress'
17791         };
17792         
17793         
17794         if(this.striped){
17795             cfg.cls += ' progress-striped';
17796         }
17797       
17798         if(this.active){
17799             cfg.cls += ' active';
17800         }
17801         
17802         
17803         return cfg;
17804     }
17805    
17806 });
17807
17808  
17809
17810  /*
17811  * - LGPL
17812  *
17813  * ProgressBar
17814  * 
17815  */
17816
17817 /**
17818  * @class Roo.bootstrap.ProgressBar
17819  * @extends Roo.bootstrap.Component
17820  * Bootstrap ProgressBar class
17821  * @cfg {Number} aria_valuenow aria-value now
17822  * @cfg {Number} aria_valuemin aria-value min
17823  * @cfg {Number} aria_valuemax aria-value max
17824  * @cfg {String} label label for the progress bar
17825  * @cfg {String} panel (success | info | warning | danger )
17826  * @cfg {String} role role of the progress bar
17827  * @cfg {String} sr_only text
17828  * 
17829  * 
17830  * @constructor
17831  * Create a new ProgressBar
17832  * @param {Object} config The config object
17833  */
17834
17835 Roo.bootstrap.ProgressBar = function(config){
17836     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17837 };
17838
17839 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17840     
17841     aria_valuenow : 0,
17842     aria_valuemin : 0,
17843     aria_valuemax : 100,
17844     label : false,
17845     panel : false,
17846     role : false,
17847     sr_only: false,
17848     
17849     getAutoCreate : function()
17850     {
17851         
17852         var cfg = {
17853             tag: 'div',
17854             cls: 'progress-bar',
17855             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17856         };
17857         
17858         if(this.sr_only){
17859             cfg.cn = {
17860                 tag: 'span',
17861                 cls: 'sr-only',
17862                 html: this.sr_only
17863             }
17864         }
17865         
17866         if(this.role){
17867             cfg.role = this.role;
17868         }
17869         
17870         if(this.aria_valuenow){
17871             cfg['aria-valuenow'] = this.aria_valuenow;
17872         }
17873         
17874         if(this.aria_valuemin){
17875             cfg['aria-valuemin'] = this.aria_valuemin;
17876         }
17877         
17878         if(this.aria_valuemax){
17879             cfg['aria-valuemax'] = this.aria_valuemax;
17880         }
17881         
17882         if(this.label && !this.sr_only){
17883             cfg.html = this.label;
17884         }
17885         
17886         if(this.panel){
17887             cfg.cls += ' progress-bar-' + this.panel;
17888         }
17889         
17890         return cfg;
17891     },
17892     
17893     update : function(aria_valuenow)
17894     {
17895         this.aria_valuenow = aria_valuenow;
17896         
17897         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17898     }
17899    
17900 });
17901
17902  
17903
17904  /*
17905  * - LGPL
17906  *
17907  * column
17908  * 
17909  */
17910
17911 /**
17912  * @class Roo.bootstrap.TabGroup
17913  * @extends Roo.bootstrap.Column
17914  * Bootstrap Column class
17915  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17916  * @cfg {Boolean} carousel true to make the group behave like a carousel
17917  * @cfg {Boolean} bullets show bullets for the panels
17918  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17919  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17920  * @cfg {Boolean} showarrow (true|false) show arrow default true
17921  * 
17922  * @constructor
17923  * Create a new TabGroup
17924  * @param {Object} config The config object
17925  */
17926
17927 Roo.bootstrap.TabGroup = function(config){
17928     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17929     if (!this.navId) {
17930         this.navId = Roo.id();
17931     }
17932     this.tabs = [];
17933     Roo.bootstrap.TabGroup.register(this);
17934     
17935 };
17936
17937 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17938     
17939     carousel : false,
17940     transition : false,
17941     bullets : 0,
17942     timer : 0,
17943     autoslide : false,
17944     slideFn : false,
17945     slideOnTouch : false,
17946     showarrow : true,
17947     
17948     getAutoCreate : function()
17949     {
17950         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17951         
17952         cfg.cls += ' tab-content';
17953         
17954         if (this.carousel) {
17955             cfg.cls += ' carousel slide';
17956             
17957             cfg.cn = [{
17958                cls : 'carousel-inner',
17959                cn : []
17960             }];
17961         
17962             if(this.bullets  && !Roo.isTouch){
17963                 
17964                 var bullets = {
17965                     cls : 'carousel-bullets',
17966                     cn : []
17967                 };
17968                
17969                 if(this.bullets_cls){
17970                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17971                 }
17972                 
17973                 bullets.cn.push({
17974                     cls : 'clear'
17975                 });
17976                 
17977                 cfg.cn[0].cn.push(bullets);
17978             }
17979             
17980             if(this.showarrow){
17981                 cfg.cn[0].cn.push({
17982                     tag : 'div',
17983                     class : 'carousel-arrow',
17984                     cn : [
17985                         {
17986                             tag : 'div',
17987                             class : 'carousel-prev',
17988                             cn : [
17989                                 {
17990                                     tag : 'i',
17991                                     class : 'fa fa-chevron-left'
17992                                 }
17993                             ]
17994                         },
17995                         {
17996                             tag : 'div',
17997                             class : 'carousel-next',
17998                             cn : [
17999                                 {
18000                                     tag : 'i',
18001                                     class : 'fa fa-chevron-right'
18002                                 }
18003                             ]
18004                         }
18005                     ]
18006                 });
18007             }
18008             
18009         }
18010         
18011         return cfg;
18012     },
18013     
18014     initEvents:  function()
18015     {
18016 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18017 //            this.el.on("touchstart", this.onTouchStart, this);
18018 //        }
18019         
18020         if(this.autoslide){
18021             var _this = this;
18022             
18023             this.slideFn = window.setInterval(function() {
18024                 _this.showPanelNext();
18025             }, this.timer);
18026         }
18027         
18028         if(this.showarrow){
18029             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18030             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18031         }
18032         
18033         
18034     },
18035     
18036 //    onTouchStart : function(e, el, o)
18037 //    {
18038 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18039 //            return;
18040 //        }
18041 //        
18042 //        this.showPanelNext();
18043 //    },
18044     
18045     
18046     getChildContainer : function()
18047     {
18048         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18049     },
18050     
18051     /**
18052     * register a Navigation item
18053     * @param {Roo.bootstrap.NavItem} the navitem to add
18054     */
18055     register : function(item)
18056     {
18057         this.tabs.push( item);
18058         item.navId = this.navId; // not really needed..
18059         this.addBullet();
18060     
18061     },
18062     
18063     getActivePanel : function()
18064     {
18065         var r = false;
18066         Roo.each(this.tabs, function(t) {
18067             if (t.active) {
18068                 r = t;
18069                 return false;
18070             }
18071             return null;
18072         });
18073         return r;
18074         
18075     },
18076     getPanelByName : function(n)
18077     {
18078         var r = false;
18079         Roo.each(this.tabs, function(t) {
18080             if (t.tabId == n) {
18081                 r = t;
18082                 return false;
18083             }
18084             return null;
18085         });
18086         return r;
18087     },
18088     indexOfPanel : function(p)
18089     {
18090         var r = false;
18091         Roo.each(this.tabs, function(t,i) {
18092             if (t.tabId == p.tabId) {
18093                 r = i;
18094                 return false;
18095             }
18096             return null;
18097         });
18098         return r;
18099     },
18100     /**
18101      * show a specific panel
18102      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18103      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18104      */
18105     showPanel : function (pan)
18106     {
18107         if(this.transition || typeof(pan) == 'undefined'){
18108             Roo.log("waiting for the transitionend");
18109             return;
18110         }
18111         
18112         if (typeof(pan) == 'number') {
18113             pan = this.tabs[pan];
18114         }
18115         
18116         if (typeof(pan) == 'string') {
18117             pan = this.getPanelByName(pan);
18118         }
18119         
18120         var cur = this.getActivePanel();
18121         
18122         if(!pan || !cur){
18123             Roo.log('pan or acitve pan is undefined');
18124             return false;
18125         }
18126         
18127         if (pan.tabId == this.getActivePanel().tabId) {
18128             return true;
18129         }
18130         
18131         if (false === cur.fireEvent('beforedeactivate')) {
18132             return false;
18133         }
18134         
18135         if(this.bullets > 0 && !Roo.isTouch){
18136             this.setActiveBullet(this.indexOfPanel(pan));
18137         }
18138         
18139         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18140             
18141             this.transition = true;
18142             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18143             var lr = dir == 'next' ? 'left' : 'right';
18144             pan.el.addClass(dir); // or prev
18145             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18146             cur.el.addClass(lr); // or right
18147             pan.el.addClass(lr);
18148             
18149             var _this = this;
18150             cur.el.on('transitionend', function() {
18151                 Roo.log("trans end?");
18152                 
18153                 pan.el.removeClass([lr,dir]);
18154                 pan.setActive(true);
18155                 
18156                 cur.el.removeClass([lr]);
18157                 cur.setActive(false);
18158                 
18159                 _this.transition = false;
18160                 
18161             }, this, { single:  true } );
18162             
18163             return true;
18164         }
18165         
18166         cur.setActive(false);
18167         pan.setActive(true);
18168         
18169         return true;
18170         
18171     },
18172     showPanelNext : function()
18173     {
18174         var i = this.indexOfPanel(this.getActivePanel());
18175         
18176         if (i >= this.tabs.length - 1 && !this.autoslide) {
18177             return;
18178         }
18179         
18180         if (i >= this.tabs.length - 1 && this.autoslide) {
18181             i = -1;
18182         }
18183         
18184         this.showPanel(this.tabs[i+1]);
18185     },
18186     
18187     showPanelPrev : function()
18188     {
18189         var i = this.indexOfPanel(this.getActivePanel());
18190         
18191         if (i  < 1 && !this.autoslide) {
18192             return;
18193         }
18194         
18195         if (i < 1 && this.autoslide) {
18196             i = this.tabs.length;
18197         }
18198         
18199         this.showPanel(this.tabs[i-1]);
18200     },
18201     
18202     
18203     addBullet: function()
18204     {
18205         if(!this.bullets || Roo.isTouch){
18206             return;
18207         }
18208         var ctr = this.el.select('.carousel-bullets',true).first();
18209         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18210         var bullet = ctr.createChild({
18211             cls : 'bullet bullet-' + i
18212         },ctr.dom.lastChild);
18213         
18214         
18215         var _this = this;
18216         
18217         bullet.on('click', (function(e, el, o, ii, t){
18218
18219             e.preventDefault();
18220
18221             this.showPanel(ii);
18222
18223             if(this.autoslide && this.slideFn){
18224                 clearInterval(this.slideFn);
18225                 this.slideFn = window.setInterval(function() {
18226                     _this.showPanelNext();
18227                 }, this.timer);
18228             }
18229
18230         }).createDelegate(this, [i, bullet], true));
18231                 
18232         
18233     },
18234      
18235     setActiveBullet : function(i)
18236     {
18237         if(Roo.isTouch){
18238             return;
18239         }
18240         
18241         Roo.each(this.el.select('.bullet', true).elements, function(el){
18242             el.removeClass('selected');
18243         });
18244
18245         var bullet = this.el.select('.bullet-' + i, true).first();
18246         
18247         if(!bullet){
18248             return;
18249         }
18250         
18251         bullet.addClass('selected');
18252     }
18253     
18254     
18255   
18256 });
18257
18258  
18259
18260  
18261  
18262 Roo.apply(Roo.bootstrap.TabGroup, {
18263     
18264     groups: {},
18265      /**
18266     * register a Navigation Group
18267     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18268     */
18269     register : function(navgrp)
18270     {
18271         this.groups[navgrp.navId] = navgrp;
18272         
18273     },
18274     /**
18275     * fetch a Navigation Group based on the navigation ID
18276     * if one does not exist , it will get created.
18277     * @param {string} the navgroup to add
18278     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18279     */
18280     get: function(navId) {
18281         if (typeof(this.groups[navId]) == 'undefined') {
18282             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18283         }
18284         return this.groups[navId] ;
18285     }
18286     
18287     
18288     
18289 });
18290
18291  /*
18292  * - LGPL
18293  *
18294  * TabPanel
18295  * 
18296  */
18297
18298 /**
18299  * @class Roo.bootstrap.TabPanel
18300  * @extends Roo.bootstrap.Component
18301  * Bootstrap TabPanel class
18302  * @cfg {Boolean} active panel active
18303  * @cfg {String} html panel content
18304  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18305  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18306  * @cfg {String} href click to link..
18307  * 
18308  * 
18309  * @constructor
18310  * Create a new TabPanel
18311  * @param {Object} config The config object
18312  */
18313
18314 Roo.bootstrap.TabPanel = function(config){
18315     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18316     this.addEvents({
18317         /**
18318              * @event changed
18319              * Fires when the active status changes
18320              * @param {Roo.bootstrap.TabPanel} this
18321              * @param {Boolean} state the new state
18322             
18323          */
18324         'changed': true,
18325         /**
18326              * @event beforedeactivate
18327              * Fires before a tab is de-activated - can be used to do validation on a form.
18328              * @param {Roo.bootstrap.TabPanel} this
18329              * @return {Boolean} false if there is an error
18330             
18331          */
18332         'beforedeactivate': true
18333      });
18334     
18335     this.tabId = this.tabId || Roo.id();
18336   
18337 };
18338
18339 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18340     
18341     active: false,
18342     html: false,
18343     tabId: false,
18344     navId : false,
18345     href : '',
18346     
18347     getAutoCreate : function(){
18348         var cfg = {
18349             tag: 'div',
18350             // item is needed for carousel - not sure if it has any effect otherwise
18351             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18352             html: this.html || ''
18353         };
18354         
18355         if(this.active){
18356             cfg.cls += ' active';
18357         }
18358         
18359         if(this.tabId){
18360             cfg.tabId = this.tabId;
18361         }
18362         
18363         
18364         return cfg;
18365     },
18366     
18367     initEvents:  function()
18368     {
18369         var p = this.parent();
18370         
18371         this.navId = this.navId || p.navId;
18372         
18373         if (typeof(this.navId) != 'undefined') {
18374             // not really needed.. but just in case.. parent should be a NavGroup.
18375             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18376             
18377             tg.register(this);
18378             
18379             var i = tg.tabs.length - 1;
18380             
18381             if(this.active && tg.bullets > 0 && i < tg.bullets){
18382                 tg.setActiveBullet(i);
18383             }
18384         }
18385         
18386         this.el.on('click', this.onClick, this);
18387         
18388         if(Roo.isTouch){
18389             this.el.on("touchstart", this.onTouchStart, this);
18390             this.el.on("touchmove", this.onTouchMove, this);
18391             this.el.on("touchend", this.onTouchEnd, this);
18392         }
18393         
18394     },
18395     
18396     onRender : function(ct, position)
18397     {
18398         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18399     },
18400     
18401     setActive : function(state)
18402     {
18403         Roo.log("panel - set active " + this.tabId + "=" + state);
18404         
18405         this.active = state;
18406         if (!state) {
18407             this.el.removeClass('active');
18408             
18409         } else  if (!this.el.hasClass('active')) {
18410             this.el.addClass('active');
18411         }
18412         
18413         this.fireEvent('changed', this, state);
18414     },
18415     
18416     onClick : function(e)
18417     {
18418         e.preventDefault();
18419         
18420         if(!this.href.length){
18421             return;
18422         }
18423         
18424         window.location.href = this.href;
18425     },
18426     
18427     startX : 0,
18428     startY : 0,
18429     endX : 0,
18430     endY : 0,
18431     swiping : false,
18432     
18433     onTouchStart : function(e)
18434     {
18435         this.swiping = false;
18436         
18437         this.startX = e.browserEvent.touches[0].clientX;
18438         this.startY = e.browserEvent.touches[0].clientY;
18439     },
18440     
18441     onTouchMove : function(e)
18442     {
18443         this.swiping = true;
18444         
18445         this.endX = e.browserEvent.touches[0].clientX;
18446         this.endY = e.browserEvent.touches[0].clientY;
18447     },
18448     
18449     onTouchEnd : function(e)
18450     {
18451         if(!this.swiping){
18452             this.onClick(e);
18453             return;
18454         }
18455         
18456         var tabGroup = this.parent();
18457         
18458         if(this.endX > this.startX){ // swiping right
18459             tabGroup.showPanelPrev();
18460             return;
18461         }
18462         
18463         if(this.startX > this.endX){ // swiping left
18464             tabGroup.showPanelNext();
18465             return;
18466         }
18467     }
18468     
18469     
18470 });
18471  
18472
18473  
18474
18475  /*
18476  * - LGPL
18477  *
18478  * DateField
18479  * 
18480  */
18481
18482 /**
18483  * @class Roo.bootstrap.DateField
18484  * @extends Roo.bootstrap.Input
18485  * Bootstrap DateField class
18486  * @cfg {Number} weekStart default 0
18487  * @cfg {String} viewMode default empty, (months|years)
18488  * @cfg {String} minViewMode default empty, (months|years)
18489  * @cfg {Number} startDate default -Infinity
18490  * @cfg {Number} endDate default Infinity
18491  * @cfg {Boolean} todayHighlight default false
18492  * @cfg {Boolean} todayBtn default false
18493  * @cfg {Boolean} calendarWeeks default false
18494  * @cfg {Object} daysOfWeekDisabled default empty
18495  * @cfg {Boolean} singleMode default false (true | false)
18496  * 
18497  * @cfg {Boolean} keyboardNavigation default true
18498  * @cfg {String} language default en
18499  * 
18500  * @constructor
18501  * Create a new DateField
18502  * @param {Object} config The config object
18503  */
18504
18505 Roo.bootstrap.DateField = function(config){
18506     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18507      this.addEvents({
18508             /**
18509              * @event show
18510              * Fires when this field show.
18511              * @param {Roo.bootstrap.DateField} this
18512              * @param {Mixed} date The date value
18513              */
18514             show : true,
18515             /**
18516              * @event show
18517              * Fires when this field hide.
18518              * @param {Roo.bootstrap.DateField} this
18519              * @param {Mixed} date The date value
18520              */
18521             hide : true,
18522             /**
18523              * @event select
18524              * Fires when select a date.
18525              * @param {Roo.bootstrap.DateField} this
18526              * @param {Mixed} date The date value
18527              */
18528             select : true,
18529             /**
18530              * @event beforeselect
18531              * Fires when before select a date.
18532              * @param {Roo.bootstrap.DateField} this
18533              * @param {Mixed} date The date value
18534              */
18535             beforeselect : true
18536         });
18537 };
18538
18539 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18540     
18541     /**
18542      * @cfg {String} format
18543      * The default date format string which can be overriden for localization support.  The format must be
18544      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18545      */
18546     format : "m/d/y",
18547     /**
18548      * @cfg {String} altFormats
18549      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18550      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18551      */
18552     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18553     
18554     weekStart : 0,
18555     
18556     viewMode : '',
18557     
18558     minViewMode : '',
18559     
18560     todayHighlight : false,
18561     
18562     todayBtn: false,
18563     
18564     language: 'en',
18565     
18566     keyboardNavigation: true,
18567     
18568     calendarWeeks: false,
18569     
18570     startDate: -Infinity,
18571     
18572     endDate: Infinity,
18573     
18574     daysOfWeekDisabled: [],
18575     
18576     _events: [],
18577     
18578     singleMode : false,
18579     
18580     UTCDate: function()
18581     {
18582         return new Date(Date.UTC.apply(Date, arguments));
18583     },
18584     
18585     UTCToday: function()
18586     {
18587         var today = new Date();
18588         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18589     },
18590     
18591     getDate: function() {
18592             var d = this.getUTCDate();
18593             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18594     },
18595     
18596     getUTCDate: function() {
18597             return this.date;
18598     },
18599     
18600     setDate: function(d) {
18601             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18602     },
18603     
18604     setUTCDate: function(d) {
18605             this.date = d;
18606             this.setValue(this.formatDate(this.date));
18607     },
18608         
18609     onRender: function(ct, position)
18610     {
18611         
18612         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18613         
18614         this.language = this.language || 'en';
18615         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18616         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18617         
18618         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18619         this.format = this.format || 'm/d/y';
18620         this.isInline = false;
18621         this.isInput = true;
18622         this.component = this.el.select('.add-on', true).first() || false;
18623         this.component = (this.component && this.component.length === 0) ? false : this.component;
18624         this.hasInput = this.component && this.inputEl().length;
18625         
18626         if (typeof(this.minViewMode === 'string')) {
18627             switch (this.minViewMode) {
18628                 case 'months':
18629                     this.minViewMode = 1;
18630                     break;
18631                 case 'years':
18632                     this.minViewMode = 2;
18633                     break;
18634                 default:
18635                     this.minViewMode = 0;
18636                     break;
18637             }
18638         }
18639         
18640         if (typeof(this.viewMode === 'string')) {
18641             switch (this.viewMode) {
18642                 case 'months':
18643                     this.viewMode = 1;
18644                     break;
18645                 case 'years':
18646                     this.viewMode = 2;
18647                     break;
18648                 default:
18649                     this.viewMode = 0;
18650                     break;
18651             }
18652         }
18653                 
18654         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18655         
18656 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18657         
18658         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18659         
18660         this.picker().on('mousedown', this.onMousedown, this);
18661         this.picker().on('click', this.onClick, this);
18662         
18663         this.picker().addClass('datepicker-dropdown');
18664         
18665         this.startViewMode = this.viewMode;
18666         
18667         if(this.singleMode){
18668             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18669                 v.setVisibilityMode(Roo.Element.DISPLAY);
18670                 v.hide();
18671             });
18672             
18673             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18674                 v.setStyle('width', '189px');
18675             });
18676         }
18677         
18678         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18679             if(!this.calendarWeeks){
18680                 v.remove();
18681                 return;
18682             }
18683             
18684             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18685             v.attr('colspan', function(i, val){
18686                 return parseInt(val) + 1;
18687             });
18688         });
18689                         
18690         
18691         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18692         
18693         this.setStartDate(this.startDate);
18694         this.setEndDate(this.endDate);
18695         
18696         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18697         
18698         this.fillDow();
18699         this.fillMonths();
18700         this.update();
18701         this.showMode();
18702         
18703         if(this.isInline) {
18704             this.showPopup();
18705         }
18706     },
18707     
18708     picker : function()
18709     {
18710         return this.pickerEl;
18711 //        return this.el.select('.datepicker', true).first();
18712     },
18713     
18714     fillDow: function()
18715     {
18716         var dowCnt = this.weekStart;
18717         
18718         var dow = {
18719             tag: 'tr',
18720             cn: [
18721                 
18722             ]
18723         };
18724         
18725         if(this.calendarWeeks){
18726             dow.cn.push({
18727                 tag: 'th',
18728                 cls: 'cw',
18729                 html: '&nbsp;'
18730             })
18731         }
18732         
18733         while (dowCnt < this.weekStart + 7) {
18734             dow.cn.push({
18735                 tag: 'th',
18736                 cls: 'dow',
18737                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18738             });
18739         }
18740         
18741         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18742     },
18743     
18744     fillMonths: function()
18745     {    
18746         var i = 0;
18747         var months = this.picker().select('>.datepicker-months td', true).first();
18748         
18749         months.dom.innerHTML = '';
18750         
18751         while (i < 12) {
18752             var month = {
18753                 tag: 'span',
18754                 cls: 'month',
18755                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18756             };
18757             
18758             months.createChild(month);
18759         }
18760         
18761     },
18762     
18763     update: function()
18764     {
18765         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;
18766         
18767         if (this.date < this.startDate) {
18768             this.viewDate = new Date(this.startDate);
18769         } else if (this.date > this.endDate) {
18770             this.viewDate = new Date(this.endDate);
18771         } else {
18772             this.viewDate = new Date(this.date);
18773         }
18774         
18775         this.fill();
18776     },
18777     
18778     fill: function() 
18779     {
18780         var d = new Date(this.viewDate),
18781                 year = d.getUTCFullYear(),
18782                 month = d.getUTCMonth(),
18783                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18784                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18785                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18786                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18787                 currentDate = this.date && this.date.valueOf(),
18788                 today = this.UTCToday();
18789         
18790         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18791         
18792 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18793         
18794 //        this.picker.select('>tfoot th.today').
18795 //                                              .text(dates[this.language].today)
18796 //                                              .toggle(this.todayBtn !== false);
18797     
18798         this.updateNavArrows();
18799         this.fillMonths();
18800                                                 
18801         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18802         
18803         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18804          
18805         prevMonth.setUTCDate(day);
18806         
18807         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18808         
18809         var nextMonth = new Date(prevMonth);
18810         
18811         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18812         
18813         nextMonth = nextMonth.valueOf();
18814         
18815         var fillMonths = false;
18816         
18817         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18818         
18819         while(prevMonth.valueOf() <= nextMonth) {
18820             var clsName = '';
18821             
18822             if (prevMonth.getUTCDay() === this.weekStart) {
18823                 if(fillMonths){
18824                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18825                 }
18826                     
18827                 fillMonths = {
18828                     tag: 'tr',
18829                     cn: []
18830                 };
18831                 
18832                 if(this.calendarWeeks){
18833                     // ISO 8601: First week contains first thursday.
18834                     // ISO also states week starts on Monday, but we can be more abstract here.
18835                     var
18836                     // Start of current week: based on weekstart/current date
18837                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18838                     // Thursday of this week
18839                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18840                     // First Thursday of year, year from thursday
18841                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18842                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18843                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18844                     
18845                     fillMonths.cn.push({
18846                         tag: 'td',
18847                         cls: 'cw',
18848                         html: calWeek
18849                     });
18850                 }
18851             }
18852             
18853             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18854                 clsName += ' old';
18855             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18856                 clsName += ' new';
18857             }
18858             if (this.todayHighlight &&
18859                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18860                 prevMonth.getUTCMonth() == today.getMonth() &&
18861                 prevMonth.getUTCDate() == today.getDate()) {
18862                 clsName += ' today';
18863             }
18864             
18865             if (currentDate && prevMonth.valueOf() === currentDate) {
18866                 clsName += ' active';
18867             }
18868             
18869             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18870                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18871                     clsName += ' disabled';
18872             }
18873             
18874             fillMonths.cn.push({
18875                 tag: 'td',
18876                 cls: 'day ' + clsName,
18877                 html: prevMonth.getDate()
18878             });
18879             
18880             prevMonth.setDate(prevMonth.getDate()+1);
18881         }
18882           
18883         var currentYear = this.date && this.date.getUTCFullYear();
18884         var currentMonth = this.date && this.date.getUTCMonth();
18885         
18886         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18887         
18888         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18889             v.removeClass('active');
18890             
18891             if(currentYear === year && k === currentMonth){
18892                 v.addClass('active');
18893             }
18894             
18895             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18896                 v.addClass('disabled');
18897             }
18898             
18899         });
18900         
18901         
18902         year = parseInt(year/10, 10) * 10;
18903         
18904         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18905         
18906         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18907         
18908         year -= 1;
18909         for (var i = -1; i < 11; i++) {
18910             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18911                 tag: 'span',
18912                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18913                 html: year
18914             });
18915             
18916             year += 1;
18917         }
18918     },
18919     
18920     showMode: function(dir) 
18921     {
18922         if (dir) {
18923             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18924         }
18925         
18926         Roo.each(this.picker().select('>div',true).elements, function(v){
18927             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18928             v.hide();
18929         });
18930         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18931     },
18932     
18933     place: function()
18934     {
18935         if(this.isInline) {
18936             return;
18937         }
18938         
18939         this.picker().removeClass(['bottom', 'top']);
18940         
18941         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18942             /*
18943              * place to the top of element!
18944              *
18945              */
18946             
18947             this.picker().addClass('top');
18948             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18949             
18950             return;
18951         }
18952         
18953         this.picker().addClass('bottom');
18954         
18955         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18956     },
18957     
18958     parseDate : function(value)
18959     {
18960         if(!value || value instanceof Date){
18961             return value;
18962         }
18963         var v = Date.parseDate(value, this.format);
18964         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18965             v = Date.parseDate(value, 'Y-m-d');
18966         }
18967         if(!v && this.altFormats){
18968             if(!this.altFormatsArray){
18969                 this.altFormatsArray = this.altFormats.split("|");
18970             }
18971             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18972                 v = Date.parseDate(value, this.altFormatsArray[i]);
18973             }
18974         }
18975         return v;
18976     },
18977     
18978     formatDate : function(date, fmt)
18979     {   
18980         return (!date || !(date instanceof Date)) ?
18981         date : date.dateFormat(fmt || this.format);
18982     },
18983     
18984     onFocus : function()
18985     {
18986         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18987         this.showPopup();
18988     },
18989     
18990     onBlur : function()
18991     {
18992         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18993         
18994         var d = this.inputEl().getValue();
18995         
18996         this.setValue(d);
18997                 
18998         this.hidePopup();
18999     },
19000     
19001     showPopup : function()
19002     {
19003         this.picker().show();
19004         this.update();
19005         this.place();
19006         
19007         this.fireEvent('showpopup', this, this.date);
19008     },
19009     
19010     hidePopup : function()
19011     {
19012         if(this.isInline) {
19013             return;
19014         }
19015         this.picker().hide();
19016         this.viewMode = this.startViewMode;
19017         this.showMode();
19018         
19019         this.fireEvent('hidepopup', this, this.date);
19020         
19021     },
19022     
19023     onMousedown: function(e)
19024     {
19025         e.stopPropagation();
19026         e.preventDefault();
19027     },
19028     
19029     keyup: function(e)
19030     {
19031         Roo.bootstrap.DateField.superclass.keyup.call(this);
19032         this.update();
19033     },
19034
19035     setValue: function(v)
19036     {
19037         if(this.fireEvent('beforeselect', this, v) !== false){
19038             var d = new Date(this.parseDate(v) ).clearTime();
19039         
19040             if(isNaN(d.getTime())){
19041                 this.date = this.viewDate = '';
19042                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19043                 return;
19044             }
19045
19046             v = this.formatDate(d);
19047
19048             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19049
19050             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19051
19052             this.update();
19053
19054             this.fireEvent('select', this, this.date);
19055         }
19056     },
19057     
19058     getValue: function()
19059     {
19060         return this.formatDate(this.date);
19061     },
19062     
19063     fireKey: function(e)
19064     {
19065         if (!this.picker().isVisible()){
19066             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19067                 this.showPopup();
19068             }
19069             return;
19070         }
19071         
19072         var dateChanged = false,
19073         dir, day, month,
19074         newDate, newViewDate;
19075         
19076         switch(e.keyCode){
19077             case 27: // escape
19078                 this.hidePopup();
19079                 e.preventDefault();
19080                 break;
19081             case 37: // left
19082             case 39: // right
19083                 if (!this.keyboardNavigation) {
19084                     break;
19085                 }
19086                 dir = e.keyCode == 37 ? -1 : 1;
19087                 
19088                 if (e.ctrlKey){
19089                     newDate = this.moveYear(this.date, dir);
19090                     newViewDate = this.moveYear(this.viewDate, dir);
19091                 } else if (e.shiftKey){
19092                     newDate = this.moveMonth(this.date, dir);
19093                     newViewDate = this.moveMonth(this.viewDate, dir);
19094                 } else {
19095                     newDate = new Date(this.date);
19096                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19097                     newViewDate = new Date(this.viewDate);
19098                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19099                 }
19100                 if (this.dateWithinRange(newDate)){
19101                     this.date = newDate;
19102                     this.viewDate = newViewDate;
19103                     this.setValue(this.formatDate(this.date));
19104 //                    this.update();
19105                     e.preventDefault();
19106                     dateChanged = true;
19107                 }
19108                 break;
19109             case 38: // up
19110             case 40: // down
19111                 if (!this.keyboardNavigation) {
19112                     break;
19113                 }
19114                 dir = e.keyCode == 38 ? -1 : 1;
19115                 if (e.ctrlKey){
19116                     newDate = this.moveYear(this.date, dir);
19117                     newViewDate = this.moveYear(this.viewDate, dir);
19118                 } else if (e.shiftKey){
19119                     newDate = this.moveMonth(this.date, dir);
19120                     newViewDate = this.moveMonth(this.viewDate, dir);
19121                 } else {
19122                     newDate = new Date(this.date);
19123                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19124                     newViewDate = new Date(this.viewDate);
19125                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19126                 }
19127                 if (this.dateWithinRange(newDate)){
19128                     this.date = newDate;
19129                     this.viewDate = newViewDate;
19130                     this.setValue(this.formatDate(this.date));
19131 //                    this.update();
19132                     e.preventDefault();
19133                     dateChanged = true;
19134                 }
19135                 break;
19136             case 13: // enter
19137                 this.setValue(this.formatDate(this.date));
19138                 this.hidePopup();
19139                 e.preventDefault();
19140                 break;
19141             case 9: // tab
19142                 this.setValue(this.formatDate(this.date));
19143                 this.hidePopup();
19144                 break;
19145             case 16: // shift
19146             case 17: // ctrl
19147             case 18: // alt
19148                 break;
19149             default :
19150                 this.hide();
19151                 
19152         }
19153     },
19154     
19155     
19156     onClick: function(e) 
19157     {
19158         e.stopPropagation();
19159         e.preventDefault();
19160         
19161         var target = e.getTarget();
19162         
19163         if(target.nodeName.toLowerCase() === 'i'){
19164             target = Roo.get(target).dom.parentNode;
19165         }
19166         
19167         var nodeName = target.nodeName;
19168         var className = target.className;
19169         var html = target.innerHTML;
19170         //Roo.log(nodeName);
19171         
19172         switch(nodeName.toLowerCase()) {
19173             case 'th':
19174                 switch(className) {
19175                     case 'switch':
19176                         this.showMode(1);
19177                         break;
19178                     case 'prev':
19179                     case 'next':
19180                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19181                         switch(this.viewMode){
19182                                 case 0:
19183                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19184                                         break;
19185                                 case 1:
19186                                 case 2:
19187                                         this.viewDate = this.moveYear(this.viewDate, dir);
19188                                         break;
19189                         }
19190                         this.fill();
19191                         break;
19192                     case 'today':
19193                         var date = new Date();
19194                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19195 //                        this.fill()
19196                         this.setValue(this.formatDate(this.date));
19197                         
19198                         this.hidePopup();
19199                         break;
19200                 }
19201                 break;
19202             case 'span':
19203                 if (className.indexOf('disabled') < 0) {
19204                     this.viewDate.setUTCDate(1);
19205                     if (className.indexOf('month') > -1) {
19206                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19207                     } else {
19208                         var year = parseInt(html, 10) || 0;
19209                         this.viewDate.setUTCFullYear(year);
19210                         
19211                     }
19212                     
19213                     if(this.singleMode){
19214                         this.setValue(this.formatDate(this.viewDate));
19215                         this.hidePopup();
19216                         return;
19217                     }
19218                     
19219                     this.showMode(-1);
19220                     this.fill();
19221                 }
19222                 break;
19223                 
19224             case 'td':
19225                 //Roo.log(className);
19226                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19227                     var day = parseInt(html, 10) || 1;
19228                     var year = this.viewDate.getUTCFullYear(),
19229                         month = this.viewDate.getUTCMonth();
19230
19231                     if (className.indexOf('old') > -1) {
19232                         if(month === 0 ){
19233                             month = 11;
19234                             year -= 1;
19235                         }else{
19236                             month -= 1;
19237                         }
19238                     } else if (className.indexOf('new') > -1) {
19239                         if (month == 11) {
19240                             month = 0;
19241                             year += 1;
19242                         } else {
19243                             month += 1;
19244                         }
19245                     }
19246                     //Roo.log([year,month,day]);
19247                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19248                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19249 //                    this.fill();
19250                     //Roo.log(this.formatDate(this.date));
19251                     this.setValue(this.formatDate(this.date));
19252                     this.hidePopup();
19253                 }
19254                 break;
19255         }
19256     },
19257     
19258     setStartDate: function(startDate)
19259     {
19260         this.startDate = startDate || -Infinity;
19261         if (this.startDate !== -Infinity) {
19262             this.startDate = this.parseDate(this.startDate);
19263         }
19264         this.update();
19265         this.updateNavArrows();
19266     },
19267
19268     setEndDate: function(endDate)
19269     {
19270         this.endDate = endDate || Infinity;
19271         if (this.endDate !== Infinity) {
19272             this.endDate = this.parseDate(this.endDate);
19273         }
19274         this.update();
19275         this.updateNavArrows();
19276     },
19277     
19278     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19279     {
19280         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19281         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19282             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19283         }
19284         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19285             return parseInt(d, 10);
19286         });
19287         this.update();
19288         this.updateNavArrows();
19289     },
19290     
19291     updateNavArrows: function() 
19292     {
19293         if(this.singleMode){
19294             return;
19295         }
19296         
19297         var d = new Date(this.viewDate),
19298         year = d.getUTCFullYear(),
19299         month = d.getUTCMonth();
19300         
19301         Roo.each(this.picker().select('.prev', true).elements, function(v){
19302             v.show();
19303             switch (this.viewMode) {
19304                 case 0:
19305
19306                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19307                         v.hide();
19308                     }
19309                     break;
19310                 case 1:
19311                 case 2:
19312                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19313                         v.hide();
19314                     }
19315                     break;
19316             }
19317         });
19318         
19319         Roo.each(this.picker().select('.next', true).elements, function(v){
19320             v.show();
19321             switch (this.viewMode) {
19322                 case 0:
19323
19324                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19325                         v.hide();
19326                     }
19327                     break;
19328                 case 1:
19329                 case 2:
19330                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19331                         v.hide();
19332                     }
19333                     break;
19334             }
19335         })
19336     },
19337     
19338     moveMonth: function(date, dir)
19339     {
19340         if (!dir) {
19341             return date;
19342         }
19343         var new_date = new Date(date.valueOf()),
19344         day = new_date.getUTCDate(),
19345         month = new_date.getUTCMonth(),
19346         mag = Math.abs(dir),
19347         new_month, test;
19348         dir = dir > 0 ? 1 : -1;
19349         if (mag == 1){
19350             test = dir == -1
19351             // If going back one month, make sure month is not current month
19352             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19353             ? function(){
19354                 return new_date.getUTCMonth() == month;
19355             }
19356             // If going forward one month, make sure month is as expected
19357             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19358             : function(){
19359                 return new_date.getUTCMonth() != new_month;
19360             };
19361             new_month = month + dir;
19362             new_date.setUTCMonth(new_month);
19363             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19364             if (new_month < 0 || new_month > 11) {
19365                 new_month = (new_month + 12) % 12;
19366             }
19367         } else {
19368             // For magnitudes >1, move one month at a time...
19369             for (var i=0; i<mag; i++) {
19370                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19371                 new_date = this.moveMonth(new_date, dir);
19372             }
19373             // ...then reset the day, keeping it in the new month
19374             new_month = new_date.getUTCMonth();
19375             new_date.setUTCDate(day);
19376             test = function(){
19377                 return new_month != new_date.getUTCMonth();
19378             };
19379         }
19380         // Common date-resetting loop -- if date is beyond end of month, make it
19381         // end of month
19382         while (test()){
19383             new_date.setUTCDate(--day);
19384             new_date.setUTCMonth(new_month);
19385         }
19386         return new_date;
19387     },
19388
19389     moveYear: function(date, dir)
19390     {
19391         return this.moveMonth(date, dir*12);
19392     },
19393
19394     dateWithinRange: function(date)
19395     {
19396         return date >= this.startDate && date <= this.endDate;
19397     },
19398
19399     
19400     remove: function() 
19401     {
19402         this.picker().remove();
19403     },
19404     
19405     validateValue : function(value)
19406     {
19407         if(this.getVisibilityEl().hasClass('hidden')){
19408             return true;
19409         }
19410         
19411         if(value.length < 1)  {
19412             if(this.allowBlank){
19413                 return true;
19414             }
19415             return false;
19416         }
19417         
19418         if(value.length < this.minLength){
19419             return false;
19420         }
19421         if(value.length > this.maxLength){
19422             return false;
19423         }
19424         if(this.vtype){
19425             var vt = Roo.form.VTypes;
19426             if(!vt[this.vtype](value, this)){
19427                 return false;
19428             }
19429         }
19430         if(typeof this.validator == "function"){
19431             var msg = this.validator(value);
19432             if(msg !== true){
19433                 return false;
19434             }
19435         }
19436         
19437         if(this.regex && !this.regex.test(value)){
19438             return false;
19439         }
19440         
19441         if(typeof(this.parseDate(value)) == 'undefined'){
19442             return false;
19443         }
19444         
19445         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19446             return false;
19447         }      
19448         
19449         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19450             return false;
19451         } 
19452         
19453         
19454         return true;
19455     },
19456     
19457     reset : function()
19458     {
19459         this.date = this.viewDate = '';
19460         
19461         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19462     }
19463    
19464 });
19465
19466 Roo.apply(Roo.bootstrap.DateField,  {
19467     
19468     head : {
19469         tag: 'thead',
19470         cn: [
19471         {
19472             tag: 'tr',
19473             cn: [
19474             {
19475                 tag: 'th',
19476                 cls: 'prev',
19477                 html: '<i class="fa fa-arrow-left"/>'
19478             },
19479             {
19480                 tag: 'th',
19481                 cls: 'switch',
19482                 colspan: '5'
19483             },
19484             {
19485                 tag: 'th',
19486                 cls: 'next',
19487                 html: '<i class="fa fa-arrow-right"/>'
19488             }
19489
19490             ]
19491         }
19492         ]
19493     },
19494     
19495     content : {
19496         tag: 'tbody',
19497         cn: [
19498         {
19499             tag: 'tr',
19500             cn: [
19501             {
19502                 tag: 'td',
19503                 colspan: '7'
19504             }
19505             ]
19506         }
19507         ]
19508     },
19509     
19510     footer : {
19511         tag: 'tfoot',
19512         cn: [
19513         {
19514             tag: 'tr',
19515             cn: [
19516             {
19517                 tag: 'th',
19518                 colspan: '7',
19519                 cls: 'today'
19520             }
19521                     
19522             ]
19523         }
19524         ]
19525     },
19526     
19527     dates:{
19528         en: {
19529             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19530             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19531             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19532             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19533             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19534             today: "Today"
19535         }
19536     },
19537     
19538     modes: [
19539     {
19540         clsName: 'days',
19541         navFnc: 'Month',
19542         navStep: 1
19543     },
19544     {
19545         clsName: 'months',
19546         navFnc: 'FullYear',
19547         navStep: 1
19548     },
19549     {
19550         clsName: 'years',
19551         navFnc: 'FullYear',
19552         navStep: 10
19553     }]
19554 });
19555
19556 Roo.apply(Roo.bootstrap.DateField,  {
19557   
19558     template : {
19559         tag: 'div',
19560         cls: 'datepicker dropdown-menu roo-dynamic',
19561         cn: [
19562         {
19563             tag: 'div',
19564             cls: 'datepicker-days',
19565             cn: [
19566             {
19567                 tag: 'table',
19568                 cls: 'table-condensed',
19569                 cn:[
19570                 Roo.bootstrap.DateField.head,
19571                 {
19572                     tag: 'tbody'
19573                 },
19574                 Roo.bootstrap.DateField.footer
19575                 ]
19576             }
19577             ]
19578         },
19579         {
19580             tag: 'div',
19581             cls: 'datepicker-months',
19582             cn: [
19583             {
19584                 tag: 'table',
19585                 cls: 'table-condensed',
19586                 cn:[
19587                 Roo.bootstrap.DateField.head,
19588                 Roo.bootstrap.DateField.content,
19589                 Roo.bootstrap.DateField.footer
19590                 ]
19591             }
19592             ]
19593         },
19594         {
19595             tag: 'div',
19596             cls: 'datepicker-years',
19597             cn: [
19598             {
19599                 tag: 'table',
19600                 cls: 'table-condensed',
19601                 cn:[
19602                 Roo.bootstrap.DateField.head,
19603                 Roo.bootstrap.DateField.content,
19604                 Roo.bootstrap.DateField.footer
19605                 ]
19606             }
19607             ]
19608         }
19609         ]
19610     }
19611 });
19612
19613  
19614
19615  /*
19616  * - LGPL
19617  *
19618  * TimeField
19619  * 
19620  */
19621
19622 /**
19623  * @class Roo.bootstrap.TimeField
19624  * @extends Roo.bootstrap.Input
19625  * Bootstrap DateField class
19626  * 
19627  * 
19628  * @constructor
19629  * Create a new TimeField
19630  * @param {Object} config The config object
19631  */
19632
19633 Roo.bootstrap.TimeField = function(config){
19634     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19635     this.addEvents({
19636             /**
19637              * @event show
19638              * Fires when this field show.
19639              * @param {Roo.bootstrap.DateField} thisthis
19640              * @param {Mixed} date The date value
19641              */
19642             show : true,
19643             /**
19644              * @event show
19645              * Fires when this field hide.
19646              * @param {Roo.bootstrap.DateField} this
19647              * @param {Mixed} date The date value
19648              */
19649             hide : true,
19650             /**
19651              * @event select
19652              * Fires when select a date.
19653              * @param {Roo.bootstrap.DateField} this
19654              * @param {Mixed} date The date value
19655              */
19656             select : true
19657         });
19658 };
19659
19660 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19661     
19662     /**
19663      * @cfg {String} format
19664      * The default time format string which can be overriden for localization support.  The format must be
19665      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19666      */
19667     format : "H:i",
19668        
19669     onRender: function(ct, position)
19670     {
19671         
19672         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19673                 
19674         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19675         
19676         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19677         
19678         this.pop = this.picker().select('>.datepicker-time',true).first();
19679         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19680         
19681         this.picker().on('mousedown', this.onMousedown, this);
19682         this.picker().on('click', this.onClick, this);
19683         
19684         this.picker().addClass('datepicker-dropdown');
19685     
19686         this.fillTime();
19687         this.update();
19688             
19689         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19690         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19691         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19692         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19693         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19694         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19695
19696     },
19697     
19698     fireKey: function(e){
19699         if (!this.picker().isVisible()){
19700             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19701                 this.show();
19702             }
19703             return;
19704         }
19705
19706         e.preventDefault();
19707         
19708         switch(e.keyCode){
19709             case 27: // escape
19710                 this.hide();
19711                 break;
19712             case 37: // left
19713             case 39: // right
19714                 this.onTogglePeriod();
19715                 break;
19716             case 38: // up
19717                 this.onIncrementMinutes();
19718                 break;
19719             case 40: // down
19720                 this.onDecrementMinutes();
19721                 break;
19722             case 13: // enter
19723             case 9: // tab
19724                 this.setTime();
19725                 break;
19726         }
19727     },
19728     
19729     onClick: function(e) {
19730         e.stopPropagation();
19731         e.preventDefault();
19732     },
19733     
19734     picker : function()
19735     {
19736         return this.el.select('.datepicker', true).first();
19737     },
19738     
19739     fillTime: function()
19740     {    
19741         var time = this.pop.select('tbody', true).first();
19742         
19743         time.dom.innerHTML = '';
19744         
19745         time.createChild({
19746             tag: 'tr',
19747             cn: [
19748                 {
19749                     tag: 'td',
19750                     cn: [
19751                         {
19752                             tag: 'a',
19753                             href: '#',
19754                             cls: 'btn',
19755                             cn: [
19756                                 {
19757                                     tag: 'span',
19758                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19759                                 }
19760                             ]
19761                         } 
19762                     ]
19763                 },
19764                 {
19765                     tag: 'td',
19766                     cls: 'separator'
19767                 },
19768                 {
19769                     tag: 'td',
19770                     cn: [
19771                         {
19772                             tag: 'a',
19773                             href: '#',
19774                             cls: 'btn',
19775                             cn: [
19776                                 {
19777                                     tag: 'span',
19778                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19779                                 }
19780                             ]
19781                         }
19782                     ]
19783                 },
19784                 {
19785                     tag: 'td',
19786                     cls: 'separator'
19787                 }
19788             ]
19789         });
19790         
19791         time.createChild({
19792             tag: 'tr',
19793             cn: [
19794                 {
19795                     tag: 'td',
19796                     cn: [
19797                         {
19798                             tag: 'span',
19799                             cls: 'timepicker-hour',
19800                             html: '00'
19801                         }  
19802                     ]
19803                 },
19804                 {
19805                     tag: 'td',
19806                     cls: 'separator',
19807                     html: ':'
19808                 },
19809                 {
19810                     tag: 'td',
19811                     cn: [
19812                         {
19813                             tag: 'span',
19814                             cls: 'timepicker-minute',
19815                             html: '00'
19816                         }  
19817                     ]
19818                 },
19819                 {
19820                     tag: 'td',
19821                     cls: 'separator'
19822                 },
19823                 {
19824                     tag: 'td',
19825                     cn: [
19826                         {
19827                             tag: 'button',
19828                             type: 'button',
19829                             cls: 'btn btn-primary period',
19830                             html: 'AM'
19831                             
19832                         }
19833                     ]
19834                 }
19835             ]
19836         });
19837         
19838         time.createChild({
19839             tag: 'tr',
19840             cn: [
19841                 {
19842                     tag: 'td',
19843                     cn: [
19844                         {
19845                             tag: 'a',
19846                             href: '#',
19847                             cls: 'btn',
19848                             cn: [
19849                                 {
19850                                     tag: 'span',
19851                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19852                                 }
19853                             ]
19854                         }
19855                     ]
19856                 },
19857                 {
19858                     tag: 'td',
19859                     cls: 'separator'
19860                 },
19861                 {
19862                     tag: 'td',
19863                     cn: [
19864                         {
19865                             tag: 'a',
19866                             href: '#',
19867                             cls: 'btn',
19868                             cn: [
19869                                 {
19870                                     tag: 'span',
19871                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19872                                 }
19873                             ]
19874                         }
19875                     ]
19876                 },
19877                 {
19878                     tag: 'td',
19879                     cls: 'separator'
19880                 }
19881             ]
19882         });
19883         
19884     },
19885     
19886     update: function()
19887     {
19888         
19889         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19890         
19891         this.fill();
19892     },
19893     
19894     fill: function() 
19895     {
19896         var hours = this.time.getHours();
19897         var minutes = this.time.getMinutes();
19898         var period = 'AM';
19899         
19900         if(hours > 11){
19901             period = 'PM';
19902         }
19903         
19904         if(hours == 0){
19905             hours = 12;
19906         }
19907         
19908         
19909         if(hours > 12){
19910             hours = hours - 12;
19911         }
19912         
19913         if(hours < 10){
19914             hours = '0' + hours;
19915         }
19916         
19917         if(minutes < 10){
19918             minutes = '0' + minutes;
19919         }
19920         
19921         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19922         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19923         this.pop.select('button', true).first().dom.innerHTML = period;
19924         
19925     },
19926     
19927     place: function()
19928     {   
19929         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19930         
19931         var cls = ['bottom'];
19932         
19933         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19934             cls.pop();
19935             cls.push('top');
19936         }
19937         
19938         cls.push('right');
19939         
19940         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19941             cls.pop();
19942             cls.push('left');
19943         }
19944         
19945         this.picker().addClass(cls.join('-'));
19946         
19947         var _this = this;
19948         
19949         Roo.each(cls, function(c){
19950             if(c == 'bottom'){
19951                 _this.picker().setTop(_this.inputEl().getHeight());
19952                 return;
19953             }
19954             if(c == 'top'){
19955                 _this.picker().setTop(0 - _this.picker().getHeight());
19956                 return;
19957             }
19958             
19959             if(c == 'left'){
19960                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19961                 return;
19962             }
19963             if(c == 'right'){
19964                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19965                 return;
19966             }
19967         });
19968         
19969     },
19970   
19971     onFocus : function()
19972     {
19973         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19974         this.show();
19975     },
19976     
19977     onBlur : function()
19978     {
19979         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19980         this.hide();
19981     },
19982     
19983     show : function()
19984     {
19985         this.picker().show();
19986         this.pop.show();
19987         this.update();
19988         this.place();
19989         
19990         this.fireEvent('show', this, this.date);
19991     },
19992     
19993     hide : function()
19994     {
19995         this.picker().hide();
19996         this.pop.hide();
19997         
19998         this.fireEvent('hide', this, this.date);
19999     },
20000     
20001     setTime : function()
20002     {
20003         this.hide();
20004         this.setValue(this.time.format(this.format));
20005         
20006         this.fireEvent('select', this, this.date);
20007         
20008         
20009     },
20010     
20011     onMousedown: function(e){
20012         e.stopPropagation();
20013         e.preventDefault();
20014     },
20015     
20016     onIncrementHours: function()
20017     {
20018         Roo.log('onIncrementHours');
20019         this.time = this.time.add(Date.HOUR, 1);
20020         this.update();
20021         
20022     },
20023     
20024     onDecrementHours: function()
20025     {
20026         Roo.log('onDecrementHours');
20027         this.time = this.time.add(Date.HOUR, -1);
20028         this.update();
20029     },
20030     
20031     onIncrementMinutes: function()
20032     {
20033         Roo.log('onIncrementMinutes');
20034         this.time = this.time.add(Date.MINUTE, 1);
20035         this.update();
20036     },
20037     
20038     onDecrementMinutes: function()
20039     {
20040         Roo.log('onDecrementMinutes');
20041         this.time = this.time.add(Date.MINUTE, -1);
20042         this.update();
20043     },
20044     
20045     onTogglePeriod: function()
20046     {
20047         Roo.log('onTogglePeriod');
20048         this.time = this.time.add(Date.HOUR, 12);
20049         this.update();
20050     }
20051     
20052    
20053 });
20054
20055 Roo.apply(Roo.bootstrap.TimeField,  {
20056     
20057     content : {
20058         tag: 'tbody',
20059         cn: [
20060             {
20061                 tag: 'tr',
20062                 cn: [
20063                 {
20064                     tag: 'td',
20065                     colspan: '7'
20066                 }
20067                 ]
20068             }
20069         ]
20070     },
20071     
20072     footer : {
20073         tag: 'tfoot',
20074         cn: [
20075             {
20076                 tag: 'tr',
20077                 cn: [
20078                 {
20079                     tag: 'th',
20080                     colspan: '7',
20081                     cls: '',
20082                     cn: [
20083                         {
20084                             tag: 'button',
20085                             cls: 'btn btn-info ok',
20086                             html: 'OK'
20087                         }
20088                     ]
20089                 }
20090
20091                 ]
20092             }
20093         ]
20094     }
20095 });
20096
20097 Roo.apply(Roo.bootstrap.TimeField,  {
20098   
20099     template : {
20100         tag: 'div',
20101         cls: 'datepicker dropdown-menu',
20102         cn: [
20103             {
20104                 tag: 'div',
20105                 cls: 'datepicker-time',
20106                 cn: [
20107                 {
20108                     tag: 'table',
20109                     cls: 'table-condensed',
20110                     cn:[
20111                     Roo.bootstrap.TimeField.content,
20112                     Roo.bootstrap.TimeField.footer
20113                     ]
20114                 }
20115                 ]
20116             }
20117         ]
20118     }
20119 });
20120
20121  
20122
20123  /*
20124  * - LGPL
20125  *
20126  * MonthField
20127  * 
20128  */
20129
20130 /**
20131  * @class Roo.bootstrap.MonthField
20132  * @extends Roo.bootstrap.Input
20133  * Bootstrap MonthField class
20134  * 
20135  * @cfg {String} language default en
20136  * 
20137  * @constructor
20138  * Create a new MonthField
20139  * @param {Object} config The config object
20140  */
20141
20142 Roo.bootstrap.MonthField = function(config){
20143     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20144     
20145     this.addEvents({
20146         /**
20147          * @event show
20148          * Fires when this field show.
20149          * @param {Roo.bootstrap.MonthField} this
20150          * @param {Mixed} date The date value
20151          */
20152         show : true,
20153         /**
20154          * @event show
20155          * Fires when this field hide.
20156          * @param {Roo.bootstrap.MonthField} this
20157          * @param {Mixed} date The date value
20158          */
20159         hide : true,
20160         /**
20161          * @event select
20162          * Fires when select a date.
20163          * @param {Roo.bootstrap.MonthField} this
20164          * @param {String} oldvalue The old value
20165          * @param {String} newvalue The new value
20166          */
20167         select : true
20168     });
20169 };
20170
20171 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20172     
20173     onRender: function(ct, position)
20174     {
20175         
20176         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20177         
20178         this.language = this.language || 'en';
20179         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20180         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20181         
20182         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20183         this.isInline = false;
20184         this.isInput = true;
20185         this.component = this.el.select('.add-on', true).first() || false;
20186         this.component = (this.component && this.component.length === 0) ? false : this.component;
20187         this.hasInput = this.component && this.inputEL().length;
20188         
20189         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20190         
20191         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20192         
20193         this.picker().on('mousedown', this.onMousedown, this);
20194         this.picker().on('click', this.onClick, this);
20195         
20196         this.picker().addClass('datepicker-dropdown');
20197         
20198         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20199             v.setStyle('width', '189px');
20200         });
20201         
20202         this.fillMonths();
20203         
20204         this.update();
20205         
20206         if(this.isInline) {
20207             this.show();
20208         }
20209         
20210     },
20211     
20212     setValue: function(v, suppressEvent)
20213     {   
20214         var o = this.getValue();
20215         
20216         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20217         
20218         this.update();
20219
20220         if(suppressEvent !== true){
20221             this.fireEvent('select', this, o, v);
20222         }
20223         
20224     },
20225     
20226     getValue: function()
20227     {
20228         return this.value;
20229     },
20230     
20231     onClick: function(e) 
20232     {
20233         e.stopPropagation();
20234         e.preventDefault();
20235         
20236         var target = e.getTarget();
20237         
20238         if(target.nodeName.toLowerCase() === 'i'){
20239             target = Roo.get(target).dom.parentNode;
20240         }
20241         
20242         var nodeName = target.nodeName;
20243         var className = target.className;
20244         var html = target.innerHTML;
20245         
20246         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20247             return;
20248         }
20249         
20250         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20251         
20252         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20253         
20254         this.hide();
20255                         
20256     },
20257     
20258     picker : function()
20259     {
20260         return this.pickerEl;
20261     },
20262     
20263     fillMonths: function()
20264     {    
20265         var i = 0;
20266         var months = this.picker().select('>.datepicker-months td', true).first();
20267         
20268         months.dom.innerHTML = '';
20269         
20270         while (i < 12) {
20271             var month = {
20272                 tag: 'span',
20273                 cls: 'month',
20274                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20275             };
20276             
20277             months.createChild(month);
20278         }
20279         
20280     },
20281     
20282     update: function()
20283     {
20284         var _this = this;
20285         
20286         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20287             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20288         }
20289         
20290         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20291             e.removeClass('active');
20292             
20293             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20294                 e.addClass('active');
20295             }
20296         })
20297     },
20298     
20299     place: function()
20300     {
20301         if(this.isInline) {
20302             return;
20303         }
20304         
20305         this.picker().removeClass(['bottom', 'top']);
20306         
20307         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20308             /*
20309              * place to the top of element!
20310              *
20311              */
20312             
20313             this.picker().addClass('top');
20314             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20315             
20316             return;
20317         }
20318         
20319         this.picker().addClass('bottom');
20320         
20321         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20322     },
20323     
20324     onFocus : function()
20325     {
20326         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20327         this.show();
20328     },
20329     
20330     onBlur : function()
20331     {
20332         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20333         
20334         var d = this.inputEl().getValue();
20335         
20336         this.setValue(d);
20337                 
20338         this.hide();
20339     },
20340     
20341     show : function()
20342     {
20343         this.picker().show();
20344         this.picker().select('>.datepicker-months', true).first().show();
20345         this.update();
20346         this.place();
20347         
20348         this.fireEvent('show', this, this.date);
20349     },
20350     
20351     hide : function()
20352     {
20353         if(this.isInline) {
20354             return;
20355         }
20356         this.picker().hide();
20357         this.fireEvent('hide', this, this.date);
20358         
20359     },
20360     
20361     onMousedown: function(e)
20362     {
20363         e.stopPropagation();
20364         e.preventDefault();
20365     },
20366     
20367     keyup: function(e)
20368     {
20369         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20370         this.update();
20371     },
20372
20373     fireKey: function(e)
20374     {
20375         if (!this.picker().isVisible()){
20376             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20377                 this.show();
20378             }
20379             return;
20380         }
20381         
20382         var dir;
20383         
20384         switch(e.keyCode){
20385             case 27: // escape
20386                 this.hide();
20387                 e.preventDefault();
20388                 break;
20389             case 37: // left
20390             case 39: // right
20391                 dir = e.keyCode == 37 ? -1 : 1;
20392                 
20393                 this.vIndex = this.vIndex + dir;
20394                 
20395                 if(this.vIndex < 0){
20396                     this.vIndex = 0;
20397                 }
20398                 
20399                 if(this.vIndex > 11){
20400                     this.vIndex = 11;
20401                 }
20402                 
20403                 if(isNaN(this.vIndex)){
20404                     this.vIndex = 0;
20405                 }
20406                 
20407                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20408                 
20409                 break;
20410             case 38: // up
20411             case 40: // down
20412                 
20413                 dir = e.keyCode == 38 ? -1 : 1;
20414                 
20415                 this.vIndex = this.vIndex + dir * 4;
20416                 
20417                 if(this.vIndex < 0){
20418                     this.vIndex = 0;
20419                 }
20420                 
20421                 if(this.vIndex > 11){
20422                     this.vIndex = 11;
20423                 }
20424                 
20425                 if(isNaN(this.vIndex)){
20426                     this.vIndex = 0;
20427                 }
20428                 
20429                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20430                 break;
20431                 
20432             case 13: // enter
20433                 
20434                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20435                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20436                 }
20437                 
20438                 this.hide();
20439                 e.preventDefault();
20440                 break;
20441             case 9: // tab
20442                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20443                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20444                 }
20445                 this.hide();
20446                 break;
20447             case 16: // shift
20448             case 17: // ctrl
20449             case 18: // alt
20450                 break;
20451             default :
20452                 this.hide();
20453                 
20454         }
20455     },
20456     
20457     remove: function() 
20458     {
20459         this.picker().remove();
20460     }
20461    
20462 });
20463
20464 Roo.apply(Roo.bootstrap.MonthField,  {
20465     
20466     content : {
20467         tag: 'tbody',
20468         cn: [
20469         {
20470             tag: 'tr',
20471             cn: [
20472             {
20473                 tag: 'td',
20474                 colspan: '7'
20475             }
20476             ]
20477         }
20478         ]
20479     },
20480     
20481     dates:{
20482         en: {
20483             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20484             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20485         }
20486     }
20487 });
20488
20489 Roo.apply(Roo.bootstrap.MonthField,  {
20490   
20491     template : {
20492         tag: 'div',
20493         cls: 'datepicker dropdown-menu roo-dynamic',
20494         cn: [
20495             {
20496                 tag: 'div',
20497                 cls: 'datepicker-months',
20498                 cn: [
20499                 {
20500                     tag: 'table',
20501                     cls: 'table-condensed',
20502                     cn:[
20503                         Roo.bootstrap.DateField.content
20504                     ]
20505                 }
20506                 ]
20507             }
20508         ]
20509     }
20510 });
20511
20512  
20513
20514  
20515  /*
20516  * - LGPL
20517  *
20518  * CheckBox
20519  * 
20520  */
20521
20522 /**
20523  * @class Roo.bootstrap.CheckBox
20524  * @extends Roo.bootstrap.Input
20525  * Bootstrap CheckBox class
20526  * 
20527  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20528  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20529  * @cfg {String} boxLabel The text that appears beside the checkbox
20530  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20531  * @cfg {Boolean} checked initnal the element
20532  * @cfg {Boolean} inline inline the element (default false)
20533  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20534  * @cfg {String} tooltip label tooltip
20535  * 
20536  * @constructor
20537  * Create a new CheckBox
20538  * @param {Object} config The config object
20539  */
20540
20541 Roo.bootstrap.CheckBox = function(config){
20542     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20543    
20544     this.addEvents({
20545         /**
20546         * @event check
20547         * Fires when the element is checked or unchecked.
20548         * @param {Roo.bootstrap.CheckBox} this This input
20549         * @param {Boolean} checked The new checked value
20550         */
20551        check : true,
20552        /**
20553         * @event click
20554         * Fires when the element is click.
20555         * @param {Roo.bootstrap.CheckBox} this This input
20556         */
20557        click : true
20558     });
20559     
20560 };
20561
20562 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20563   
20564     inputType: 'checkbox',
20565     inputValue: 1,
20566     valueOff: 0,
20567     boxLabel: false,
20568     checked: false,
20569     weight : false,
20570     inline: false,
20571     tooltip : '',
20572     
20573     getAutoCreate : function()
20574     {
20575         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20576         
20577         var id = Roo.id();
20578         
20579         var cfg = {};
20580         
20581         cfg.cls = 'form-group ' + this.inputType; //input-group
20582         
20583         if(this.inline){
20584             cfg.cls += ' ' + this.inputType + '-inline';
20585         }
20586         
20587         var input =  {
20588             tag: 'input',
20589             id : id,
20590             type : this.inputType,
20591             value : this.inputValue,
20592             cls : 'roo-' + this.inputType, //'form-box',
20593             placeholder : this.placeholder || ''
20594             
20595         };
20596         
20597         if(this.inputType != 'radio'){
20598             var hidden =  {
20599                 tag: 'input',
20600                 type : 'hidden',
20601                 cls : 'roo-hidden-value',
20602                 value : this.checked ? this.inputValue : this.valueOff
20603             };
20604         }
20605         
20606             
20607         if (this.weight) { // Validity check?
20608             cfg.cls += " " + this.inputType + "-" + this.weight;
20609         }
20610         
20611         if (this.disabled) {
20612             input.disabled=true;
20613         }
20614         
20615         if(this.checked){
20616             input.checked = this.checked;
20617         }
20618         
20619         if (this.name) {
20620             
20621             input.name = this.name;
20622             
20623             if(this.inputType != 'radio'){
20624                 hidden.name = this.name;
20625                 input.name = '_hidden_' + this.name;
20626             }
20627         }
20628         
20629         if (this.size) {
20630             input.cls += ' input-' + this.size;
20631         }
20632         
20633         var settings=this;
20634         
20635         ['xs','sm','md','lg'].map(function(size){
20636             if (settings[size]) {
20637                 cfg.cls += ' col-' + size + '-' + settings[size];
20638             }
20639         });
20640         
20641         var inputblock = input;
20642          
20643         if (this.before || this.after) {
20644             
20645             inputblock = {
20646                 cls : 'input-group',
20647                 cn :  [] 
20648             };
20649             
20650             if (this.before) {
20651                 inputblock.cn.push({
20652                     tag :'span',
20653                     cls : 'input-group-addon',
20654                     html : this.before
20655                 });
20656             }
20657             
20658             inputblock.cn.push(input);
20659             
20660             if(this.inputType != 'radio'){
20661                 inputblock.cn.push(hidden);
20662             }
20663             
20664             if (this.after) {
20665                 inputblock.cn.push({
20666                     tag :'span',
20667                     cls : 'input-group-addon',
20668                     html : this.after
20669                 });
20670             }
20671             
20672         }
20673         
20674         if (align ==='left' && this.fieldLabel.length) {
20675 //                Roo.log("left and has label");
20676             cfg.cn = [
20677                 {
20678                     tag: 'label',
20679                     'for' :  id,
20680                     cls : 'control-label',
20681                     html : this.fieldLabel
20682                 },
20683                 {
20684                     cls : "", 
20685                     cn: [
20686                         inputblock
20687                     ]
20688                 }
20689             ];
20690             
20691             if(this.labelWidth > 12){
20692                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20693             }
20694             
20695             if(this.labelWidth < 13 && this.labelmd == 0){
20696                 this.labelmd = this.labelWidth;
20697             }
20698             
20699             if(this.labellg > 0){
20700                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20701                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20702             }
20703             
20704             if(this.labelmd > 0){
20705                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20706                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20707             }
20708             
20709             if(this.labelsm > 0){
20710                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20711                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20712             }
20713             
20714             if(this.labelxs > 0){
20715                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20716                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20717             }
20718             
20719         } else if ( this.fieldLabel.length) {
20720 //                Roo.log(" label");
20721                 cfg.cn = [
20722                    
20723                     {
20724                         tag: this.boxLabel ? 'span' : 'label',
20725                         'for': id,
20726                         cls: 'control-label box-input-label',
20727                         //cls : 'input-group-addon',
20728                         html : this.fieldLabel
20729                     },
20730                     
20731                     inputblock
20732                     
20733                 ];
20734
20735         } else {
20736             
20737 //                Roo.log(" no label && no align");
20738                 cfg.cn = [  inputblock ] ;
20739                 
20740                 
20741         }
20742         
20743         if(this.boxLabel){
20744              var boxLabelCfg = {
20745                 tag: 'label',
20746                 //'for': id, // box label is handled by onclick - so no for...
20747                 cls: 'box-label',
20748                 html: this.boxLabel
20749             };
20750             
20751             if(this.tooltip){
20752                 boxLabelCfg.tooltip = this.tooltip;
20753             }
20754              
20755             cfg.cn.push(boxLabelCfg);
20756         }
20757         
20758         if(this.inputType != 'radio'){
20759             cfg.cn.push(hidden);
20760         }
20761         
20762         return cfg;
20763         
20764     },
20765     
20766     /**
20767      * return the real input element.
20768      */
20769     inputEl: function ()
20770     {
20771         return this.el.select('input.roo-' + this.inputType,true).first();
20772     },
20773     hiddenEl: function ()
20774     {
20775         return this.el.select('input.roo-hidden-value',true).first();
20776     },
20777     
20778     labelEl: function()
20779     {
20780         return this.el.select('label.control-label',true).first();
20781     },
20782     /* depricated... */
20783     
20784     label: function()
20785     {
20786         return this.labelEl();
20787     },
20788     
20789     boxLabelEl: function()
20790     {
20791         return this.el.select('label.box-label',true).first();
20792     },
20793     
20794     initEvents : function()
20795     {
20796 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20797         
20798         this.inputEl().on('click', this.onClick,  this);
20799         
20800         if (this.boxLabel) { 
20801             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20802         }
20803         
20804         this.startValue = this.getValue();
20805         
20806         if(this.groupId){
20807             Roo.bootstrap.CheckBox.register(this);
20808         }
20809     },
20810     
20811     onClick : function(e)
20812     {   
20813         if(this.fireEvent('click', this, e) !== false){
20814             this.setChecked(!this.checked);
20815         }
20816         
20817     },
20818     
20819     setChecked : function(state,suppressEvent)
20820     {
20821         this.startValue = this.getValue();
20822
20823         if(this.inputType == 'radio'){
20824             
20825             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20826                 e.dom.checked = false;
20827             });
20828             
20829             this.inputEl().dom.checked = true;
20830             
20831             this.inputEl().dom.value = this.inputValue;
20832             
20833             if(suppressEvent !== true){
20834                 this.fireEvent('check', this, true);
20835             }
20836             
20837             this.validate();
20838             
20839             return;
20840         }
20841         
20842         this.checked = state;
20843         
20844         this.inputEl().dom.checked = state;
20845         
20846         
20847         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20848         
20849         if(suppressEvent !== true){
20850             this.fireEvent('check', this, state);
20851         }
20852         
20853         this.validate();
20854     },
20855     
20856     getValue : function()
20857     {
20858         if(this.inputType == 'radio'){
20859             return this.getGroupValue();
20860         }
20861         
20862         return this.hiddenEl().dom.value;
20863         
20864     },
20865     
20866     getGroupValue : function()
20867     {
20868         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20869             return '';
20870         }
20871         
20872         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20873     },
20874     
20875     setValue : function(v,suppressEvent)
20876     {
20877         if(this.inputType == 'radio'){
20878             this.setGroupValue(v, suppressEvent);
20879             return;
20880         }
20881         
20882         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20883         
20884         this.validate();
20885     },
20886     
20887     setGroupValue : function(v, suppressEvent)
20888     {
20889         this.startValue = this.getValue();
20890         
20891         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20892             e.dom.checked = false;
20893             
20894             if(e.dom.value == v){
20895                 e.dom.checked = true;
20896             }
20897         });
20898         
20899         if(suppressEvent !== true){
20900             this.fireEvent('check', this, true);
20901         }
20902
20903         this.validate();
20904         
20905         return;
20906     },
20907     
20908     validate : function()
20909     {
20910         if(this.getVisibilityEl().hasClass('hidden')){
20911             return true;
20912         }
20913         
20914         if(
20915                 this.disabled || 
20916                 (this.inputType == 'radio' && this.validateRadio()) ||
20917                 (this.inputType == 'checkbox' && this.validateCheckbox())
20918         ){
20919             this.markValid();
20920             return true;
20921         }
20922         
20923         this.markInvalid();
20924         return false;
20925     },
20926     
20927     validateRadio : function()
20928     {
20929         if(this.getVisibilityEl().hasClass('hidden')){
20930             return true;
20931         }
20932         
20933         if(this.allowBlank){
20934             return true;
20935         }
20936         
20937         var valid = false;
20938         
20939         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20940             if(!e.dom.checked){
20941                 return;
20942             }
20943             
20944             valid = true;
20945             
20946             return false;
20947         });
20948         
20949         return valid;
20950     },
20951     
20952     validateCheckbox : function()
20953     {
20954         if(!this.groupId){
20955             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20956             //return (this.getValue() == this.inputValue) ? true : false;
20957         }
20958         
20959         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20960         
20961         if(!group){
20962             return false;
20963         }
20964         
20965         var r = false;
20966         
20967         for(var i in group){
20968             if(group[i].el.isVisible(true)){
20969                 r = false;
20970                 break;
20971             }
20972             
20973             r = true;
20974         }
20975         
20976         for(var i in group){
20977             if(r){
20978                 break;
20979             }
20980             
20981             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20982         }
20983         
20984         return r;
20985     },
20986     
20987     /**
20988      * Mark this field as valid
20989      */
20990     markValid : function()
20991     {
20992         var _this = this;
20993         
20994         this.fireEvent('valid', this);
20995         
20996         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20997         
20998         if(this.groupId){
20999             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21000         }
21001         
21002         if(label){
21003             label.markValid();
21004         }
21005
21006         if(this.inputType == 'radio'){
21007             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21008                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21009                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21010             });
21011             
21012             return;
21013         }
21014
21015         if(!this.groupId){
21016             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21017             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21018             return;
21019         }
21020         
21021         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21022         
21023         if(!group){
21024             return;
21025         }
21026         
21027         for(var i in group){
21028             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21029             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21030         }
21031     },
21032     
21033      /**
21034      * Mark this field as invalid
21035      * @param {String} msg The validation message
21036      */
21037     markInvalid : function(msg)
21038     {
21039         if(this.allowBlank){
21040             return;
21041         }
21042         
21043         var _this = this;
21044         
21045         this.fireEvent('invalid', this, msg);
21046         
21047         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21048         
21049         if(this.groupId){
21050             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21051         }
21052         
21053         if(label){
21054             label.markInvalid();
21055         }
21056             
21057         if(this.inputType == 'radio'){
21058             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21059                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21060                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21061             });
21062             
21063             return;
21064         }
21065         
21066         if(!this.groupId){
21067             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21068             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21069             return;
21070         }
21071         
21072         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21073         
21074         if(!group){
21075             return;
21076         }
21077         
21078         for(var i in group){
21079             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21080             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21081         }
21082         
21083     },
21084     
21085     clearInvalid : function()
21086     {
21087         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21088         
21089         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21090         
21091         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21092         
21093         if (label && label.iconEl) {
21094             label.iconEl.removeClass(label.validClass);
21095             label.iconEl.removeClass(label.invalidClass);
21096         }
21097     },
21098     
21099     disable : function()
21100     {
21101         if(this.inputType != 'radio'){
21102             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21103             return;
21104         }
21105         
21106         var _this = this;
21107         
21108         if(this.rendered){
21109             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21110                 _this.getActionEl().addClass(this.disabledClass);
21111                 e.dom.disabled = true;
21112             });
21113         }
21114         
21115         this.disabled = true;
21116         this.fireEvent("disable", this);
21117         return this;
21118     },
21119
21120     enable : function()
21121     {
21122         if(this.inputType != 'radio'){
21123             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21124             return;
21125         }
21126         
21127         var _this = this;
21128         
21129         if(this.rendered){
21130             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21131                 _this.getActionEl().removeClass(this.disabledClass);
21132                 e.dom.disabled = false;
21133             });
21134         }
21135         
21136         this.disabled = false;
21137         this.fireEvent("enable", this);
21138         return this;
21139     },
21140     
21141     setBoxLabel : function(v)
21142     {
21143         this.boxLabel = v;
21144         
21145         if(this.rendered){
21146             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21147         }
21148     }
21149
21150 });
21151
21152 Roo.apply(Roo.bootstrap.CheckBox, {
21153     
21154     groups: {},
21155     
21156      /**
21157     * register a CheckBox Group
21158     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21159     */
21160     register : function(checkbox)
21161     {
21162         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21163             this.groups[checkbox.groupId] = {};
21164         }
21165         
21166         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21167             return;
21168         }
21169         
21170         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21171         
21172     },
21173     /**
21174     * fetch a CheckBox Group based on the group ID
21175     * @param {string} the group ID
21176     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21177     */
21178     get: function(groupId) {
21179         if (typeof(this.groups[groupId]) == 'undefined') {
21180             return false;
21181         }
21182         
21183         return this.groups[groupId] ;
21184     }
21185     
21186     
21187 });
21188 /*
21189  * - LGPL
21190  *
21191  * RadioItem
21192  * 
21193  */
21194
21195 /**
21196  * @class Roo.bootstrap.Radio
21197  * @extends Roo.bootstrap.Component
21198  * Bootstrap Radio class
21199  * @cfg {String} boxLabel - the label associated
21200  * @cfg {String} value - the value of radio
21201  * 
21202  * @constructor
21203  * Create a new Radio
21204  * @param {Object} config The config object
21205  */
21206 Roo.bootstrap.Radio = function(config){
21207     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21208     
21209 };
21210
21211 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21212     
21213     boxLabel : '',
21214     
21215     value : '',
21216     
21217     getAutoCreate : function()
21218     {
21219         var cfg = {
21220             tag : 'div',
21221             cls : 'form-group radio',
21222             cn : [
21223                 {
21224                     tag : 'label',
21225                     cls : 'box-label',
21226                     html : this.boxLabel
21227                 }
21228             ]
21229         };
21230         
21231         return cfg;
21232     },
21233     
21234     initEvents : function() 
21235     {
21236         this.parent().register(this);
21237         
21238         this.el.on('click', this.onClick, this);
21239         
21240     },
21241     
21242     onClick : function(e)
21243     {
21244         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21245             this.setChecked(true);
21246         }
21247     },
21248     
21249     setChecked : function(state, suppressEvent)
21250     {
21251         this.parent().setValue(this.value, suppressEvent);
21252         
21253     },
21254     
21255     setBoxLabel : function(v)
21256     {
21257         this.boxLabel = v;
21258         
21259         if(this.rendered){
21260             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21261         }
21262     }
21263     
21264 });
21265  
21266
21267  /*
21268  * - LGPL
21269  *
21270  * Input
21271  * 
21272  */
21273
21274 /**
21275  * @class Roo.bootstrap.SecurePass
21276  * @extends Roo.bootstrap.Input
21277  * Bootstrap SecurePass class
21278  *
21279  * 
21280  * @constructor
21281  * Create a new SecurePass
21282  * @param {Object} config The config object
21283  */
21284  
21285 Roo.bootstrap.SecurePass = function (config) {
21286     // these go here, so the translation tool can replace them..
21287     this.errors = {
21288         PwdEmpty: "Please type a password, and then retype it to confirm.",
21289         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21290         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21291         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21292         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21293         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21294         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21295         TooWeak: "Your password is Too Weak."
21296     },
21297     this.meterLabel = "Password strength:";
21298     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21299     this.meterClass = [
21300         "roo-password-meter-tooweak", 
21301         "roo-password-meter-weak", 
21302         "roo-password-meter-medium", 
21303         "roo-password-meter-strong", 
21304         "roo-password-meter-grey"
21305     ];
21306     
21307     this.errors = {};
21308     
21309     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21310 }
21311
21312 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21313     /**
21314      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21315      * {
21316      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21317      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21318      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21319      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21320      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21321      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21322      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21323      * })
21324      */
21325     // private
21326     
21327     meterWidth: 300,
21328     errorMsg :'',    
21329     errors: false,
21330     imageRoot: '/',
21331     /**
21332      * @cfg {String/Object} Label for the strength meter (defaults to
21333      * 'Password strength:')
21334      */
21335     // private
21336     meterLabel: '',
21337     /**
21338      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21339      * ['Weak', 'Medium', 'Strong'])
21340      */
21341     // private    
21342     pwdStrengths: false,    
21343     // private
21344     strength: 0,
21345     // private
21346     _lastPwd: null,
21347     // private
21348     kCapitalLetter: 0,
21349     kSmallLetter: 1,
21350     kDigit: 2,
21351     kPunctuation: 3,
21352     
21353     insecure: false,
21354     // private
21355     initEvents: function ()
21356     {
21357         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21358
21359         if (this.el.is('input[type=password]') && Roo.isSafari) {
21360             this.el.on('keydown', this.SafariOnKeyDown, this);
21361         }
21362
21363         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21364     },
21365     // private
21366     onRender: function (ct, position)
21367     {
21368         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21369         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21370         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21371
21372         this.trigger.createChild({
21373                    cn: [
21374                     {
21375                     //id: 'PwdMeter',
21376                     tag: 'div',
21377                     cls: 'roo-password-meter-grey col-xs-12',
21378                     style: {
21379                         //width: 0,
21380                         //width: this.meterWidth + 'px'                                                
21381                         }
21382                     },
21383                     {                            
21384                          cls: 'roo-password-meter-text'                          
21385                     }
21386                 ]            
21387         });
21388
21389          
21390         if (this.hideTrigger) {
21391             this.trigger.setDisplayed(false);
21392         }
21393         this.setSize(this.width || '', this.height || '');
21394     },
21395     // private
21396     onDestroy: function ()
21397     {
21398         if (this.trigger) {
21399             this.trigger.removeAllListeners();
21400             this.trigger.remove();
21401         }
21402         if (this.wrap) {
21403             this.wrap.remove();
21404         }
21405         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21406     },
21407     // private
21408     checkStrength: function ()
21409     {
21410         var pwd = this.inputEl().getValue();
21411         if (pwd == this._lastPwd) {
21412             return;
21413         }
21414
21415         var strength;
21416         if (this.ClientSideStrongPassword(pwd)) {
21417             strength = 3;
21418         } else if (this.ClientSideMediumPassword(pwd)) {
21419             strength = 2;
21420         } else if (this.ClientSideWeakPassword(pwd)) {
21421             strength = 1;
21422         } else {
21423             strength = 0;
21424         }
21425         
21426         Roo.log('strength1: ' + strength);
21427         
21428         //var pm = this.trigger.child('div/div/div').dom;
21429         var pm = this.trigger.child('div/div');
21430         pm.removeClass(this.meterClass);
21431         pm.addClass(this.meterClass[strength]);
21432                 
21433         
21434         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21435                 
21436         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21437         
21438         this._lastPwd = pwd;
21439     },
21440     reset: function ()
21441     {
21442         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21443         
21444         this._lastPwd = '';
21445         
21446         var pm = this.trigger.child('div/div');
21447         pm.removeClass(this.meterClass);
21448         pm.addClass('roo-password-meter-grey');        
21449         
21450         
21451         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21452         
21453         pt.innerHTML = '';
21454         this.inputEl().dom.type='password';
21455     },
21456     // private
21457     validateValue: function (value)
21458     {
21459         
21460         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21461             return false;
21462         }
21463         if (value.length == 0) {
21464             if (this.allowBlank) {
21465                 this.clearInvalid();
21466                 return true;
21467             }
21468
21469             this.markInvalid(this.errors.PwdEmpty);
21470             this.errorMsg = this.errors.PwdEmpty;
21471             return false;
21472         }
21473         
21474         if(this.insecure){
21475             return true;
21476         }
21477         
21478         if ('[\x21-\x7e]*'.match(value)) {
21479             this.markInvalid(this.errors.PwdBadChar);
21480             this.errorMsg = this.errors.PwdBadChar;
21481             return false;
21482         }
21483         if (value.length < 6) {
21484             this.markInvalid(this.errors.PwdShort);
21485             this.errorMsg = this.errors.PwdShort;
21486             return false;
21487         }
21488         if (value.length > 16) {
21489             this.markInvalid(this.errors.PwdLong);
21490             this.errorMsg = this.errors.PwdLong;
21491             return false;
21492         }
21493         var strength;
21494         if (this.ClientSideStrongPassword(value)) {
21495             strength = 3;
21496         } else if (this.ClientSideMediumPassword(value)) {
21497             strength = 2;
21498         } else if (this.ClientSideWeakPassword(value)) {
21499             strength = 1;
21500         } else {
21501             strength = 0;
21502         }
21503
21504         
21505         if (strength < 2) {
21506             //this.markInvalid(this.errors.TooWeak);
21507             this.errorMsg = this.errors.TooWeak;
21508             //return false;
21509         }
21510         
21511         
21512         console.log('strength2: ' + strength);
21513         
21514         //var pm = this.trigger.child('div/div/div').dom;
21515         
21516         var pm = this.trigger.child('div/div');
21517         pm.removeClass(this.meterClass);
21518         pm.addClass(this.meterClass[strength]);
21519                 
21520         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21521                 
21522         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21523         
21524         this.errorMsg = ''; 
21525         return true;
21526     },
21527     // private
21528     CharacterSetChecks: function (type)
21529     {
21530         this.type = type;
21531         this.fResult = false;
21532     },
21533     // private
21534     isctype: function (character, type)
21535     {
21536         switch (type) {  
21537             case this.kCapitalLetter:
21538                 if (character >= 'A' && character <= 'Z') {
21539                     return true;
21540                 }
21541                 break;
21542             
21543             case this.kSmallLetter:
21544                 if (character >= 'a' && character <= 'z') {
21545                     return true;
21546                 }
21547                 break;
21548             
21549             case this.kDigit:
21550                 if (character >= '0' && character <= '9') {
21551                     return true;
21552                 }
21553                 break;
21554             
21555             case this.kPunctuation:
21556                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21557                     return true;
21558                 }
21559                 break;
21560             
21561             default:
21562                 return false;
21563         }
21564
21565     },
21566     // private
21567     IsLongEnough: function (pwd, size)
21568     {
21569         return !(pwd == null || isNaN(size) || pwd.length < size);
21570     },
21571     // private
21572     SpansEnoughCharacterSets: function (word, nb)
21573     {
21574         if (!this.IsLongEnough(word, nb))
21575         {
21576             return false;
21577         }
21578
21579         var characterSetChecks = new Array(
21580             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21581             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21582         );
21583         
21584         for (var index = 0; index < word.length; ++index) {
21585             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21586                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21587                     characterSetChecks[nCharSet].fResult = true;
21588                     break;
21589                 }
21590             }
21591         }
21592
21593         var nCharSets = 0;
21594         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21595             if (characterSetChecks[nCharSet].fResult) {
21596                 ++nCharSets;
21597             }
21598         }
21599
21600         if (nCharSets < nb) {
21601             return false;
21602         }
21603         return true;
21604     },
21605     // private
21606     ClientSideStrongPassword: function (pwd)
21607     {
21608         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21609     },
21610     // private
21611     ClientSideMediumPassword: function (pwd)
21612     {
21613         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21614     },
21615     // private
21616     ClientSideWeakPassword: function (pwd)
21617     {
21618         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21619     }
21620           
21621 })//<script type="text/javascript">
21622
21623 /*
21624  * Based  Ext JS Library 1.1.1
21625  * Copyright(c) 2006-2007, Ext JS, LLC.
21626  * LGPL
21627  *
21628  */
21629  
21630 /**
21631  * @class Roo.HtmlEditorCore
21632  * @extends Roo.Component
21633  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21634  *
21635  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21636  */
21637
21638 Roo.HtmlEditorCore = function(config){
21639     
21640     
21641     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21642     
21643     
21644     this.addEvents({
21645         /**
21646          * @event initialize
21647          * Fires when the editor is fully initialized (including the iframe)
21648          * @param {Roo.HtmlEditorCore} this
21649          */
21650         initialize: true,
21651         /**
21652          * @event activate
21653          * Fires when the editor is first receives the focus. Any insertion must wait
21654          * until after this event.
21655          * @param {Roo.HtmlEditorCore} this
21656          */
21657         activate: true,
21658          /**
21659          * @event beforesync
21660          * Fires before the textarea is updated with content from the editor iframe. Return false
21661          * to cancel the sync.
21662          * @param {Roo.HtmlEditorCore} this
21663          * @param {String} html
21664          */
21665         beforesync: true,
21666          /**
21667          * @event beforepush
21668          * Fires before the iframe editor is updated with content from the textarea. Return false
21669          * to cancel the push.
21670          * @param {Roo.HtmlEditorCore} this
21671          * @param {String} html
21672          */
21673         beforepush: true,
21674          /**
21675          * @event sync
21676          * Fires when the textarea is updated with content from the editor iframe.
21677          * @param {Roo.HtmlEditorCore} this
21678          * @param {String} html
21679          */
21680         sync: true,
21681          /**
21682          * @event push
21683          * Fires when the iframe editor is updated with content from the textarea.
21684          * @param {Roo.HtmlEditorCore} this
21685          * @param {String} html
21686          */
21687         push: true,
21688         
21689         /**
21690          * @event editorevent
21691          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21692          * @param {Roo.HtmlEditorCore} this
21693          */
21694         editorevent: true
21695         
21696     });
21697     
21698     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21699     
21700     // defaults : white / black...
21701     this.applyBlacklists();
21702     
21703     
21704     
21705 };
21706
21707
21708 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21709
21710
21711      /**
21712      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21713      */
21714     
21715     owner : false,
21716     
21717      /**
21718      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21719      *                        Roo.resizable.
21720      */
21721     resizable : false,
21722      /**
21723      * @cfg {Number} height (in pixels)
21724      */   
21725     height: 300,
21726    /**
21727      * @cfg {Number} width (in pixels)
21728      */   
21729     width: 500,
21730     
21731     /**
21732      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21733      * 
21734      */
21735     stylesheets: false,
21736     
21737     // id of frame..
21738     frameId: false,
21739     
21740     // private properties
21741     validationEvent : false,
21742     deferHeight: true,
21743     initialized : false,
21744     activated : false,
21745     sourceEditMode : false,
21746     onFocus : Roo.emptyFn,
21747     iframePad:3,
21748     hideMode:'offsets',
21749     
21750     clearUp: true,
21751     
21752     // blacklist + whitelisted elements..
21753     black: false,
21754     white: false,
21755      
21756     bodyCls : '',
21757
21758     /**
21759      * Protected method that will not generally be called directly. It
21760      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21761      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21762      */
21763     getDocMarkup : function(){
21764         // body styles..
21765         var st = '';
21766         
21767         // inherit styels from page...?? 
21768         if (this.stylesheets === false) {
21769             
21770             Roo.get(document.head).select('style').each(function(node) {
21771                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21772             });
21773             
21774             Roo.get(document.head).select('link').each(function(node) { 
21775                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21776             });
21777             
21778         } else if (!this.stylesheets.length) {
21779                 // simple..
21780                 st = '<style type="text/css">' +
21781                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21782                    '</style>';
21783         } else { 
21784             st = '<style type="text/css">' +
21785                     this.stylesheets +
21786                 '</style>';
21787         }
21788         
21789         st +=  '<style type="text/css">' +
21790             'IMG { cursor: pointer } ' +
21791         '</style>';
21792
21793         var cls = 'roo-htmleditor-body';
21794         
21795         if(this.bodyCls.length){
21796             cls += ' ' + this.bodyCls;
21797         }
21798         
21799         return '<html><head>' + st  +
21800             //<style type="text/css">' +
21801             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21802             //'</style>' +
21803             ' </head><body class="' +  cls + '"></body></html>';
21804     },
21805
21806     // private
21807     onRender : function(ct, position)
21808     {
21809         var _t = this;
21810         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21811         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21812         
21813         
21814         this.el.dom.style.border = '0 none';
21815         this.el.dom.setAttribute('tabIndex', -1);
21816         this.el.addClass('x-hidden hide');
21817         
21818         
21819         
21820         if(Roo.isIE){ // fix IE 1px bogus margin
21821             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21822         }
21823        
21824         
21825         this.frameId = Roo.id();
21826         
21827          
21828         
21829         var iframe = this.owner.wrap.createChild({
21830             tag: 'iframe',
21831             cls: 'form-control', // bootstrap..
21832             id: this.frameId,
21833             name: this.frameId,
21834             frameBorder : 'no',
21835             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21836         }, this.el
21837         );
21838         
21839         
21840         this.iframe = iframe.dom;
21841
21842          this.assignDocWin();
21843         
21844         this.doc.designMode = 'on';
21845        
21846         this.doc.open();
21847         this.doc.write(this.getDocMarkup());
21848         this.doc.close();
21849
21850         
21851         var task = { // must defer to wait for browser to be ready
21852             run : function(){
21853                 //console.log("run task?" + this.doc.readyState);
21854                 this.assignDocWin();
21855                 if(this.doc.body || this.doc.readyState == 'complete'){
21856                     try {
21857                         this.doc.designMode="on";
21858                     } catch (e) {
21859                         return;
21860                     }
21861                     Roo.TaskMgr.stop(task);
21862                     this.initEditor.defer(10, this);
21863                 }
21864             },
21865             interval : 10,
21866             duration: 10000,
21867             scope: this
21868         };
21869         Roo.TaskMgr.start(task);
21870
21871     },
21872
21873     // private
21874     onResize : function(w, h)
21875     {
21876          Roo.log('resize: ' +w + ',' + h );
21877         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21878         if(!this.iframe){
21879             return;
21880         }
21881         if(typeof w == 'number'){
21882             
21883             this.iframe.style.width = w + 'px';
21884         }
21885         if(typeof h == 'number'){
21886             
21887             this.iframe.style.height = h + 'px';
21888             if(this.doc){
21889                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21890             }
21891         }
21892         
21893     },
21894
21895     /**
21896      * Toggles the editor between standard and source edit mode.
21897      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21898      */
21899     toggleSourceEdit : function(sourceEditMode){
21900         
21901         this.sourceEditMode = sourceEditMode === true;
21902         
21903         if(this.sourceEditMode){
21904  
21905             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21906             
21907         }else{
21908             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21909             //this.iframe.className = '';
21910             this.deferFocus();
21911         }
21912         //this.setSize(this.owner.wrap.getSize());
21913         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21914     },
21915
21916     
21917   
21918
21919     /**
21920      * Protected method that will not generally be called directly. If you need/want
21921      * custom HTML cleanup, this is the method you should override.
21922      * @param {String} html The HTML to be cleaned
21923      * return {String} The cleaned HTML
21924      */
21925     cleanHtml : function(html){
21926         html = String(html);
21927         if(html.length > 5){
21928             if(Roo.isSafari){ // strip safari nonsense
21929                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21930             }
21931         }
21932         if(html == '&nbsp;'){
21933             html = '';
21934         }
21935         return html;
21936     },
21937
21938     /**
21939      * HTML Editor -> Textarea
21940      * Protected method that will not generally be called directly. Syncs the contents
21941      * of the editor iframe with the textarea.
21942      */
21943     syncValue : function(){
21944         if(this.initialized){
21945             var bd = (this.doc.body || this.doc.documentElement);
21946             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21947             var html = bd.innerHTML;
21948             if(Roo.isSafari){
21949                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21950                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21951                 if(m && m[1]){
21952                     html = '<div style="'+m[0]+'">' + html + '</div>';
21953                 }
21954             }
21955             html = this.cleanHtml(html);
21956             // fix up the special chars.. normaly like back quotes in word...
21957             // however we do not want to do this with chinese..
21958             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21959                 var cc = b.charCodeAt();
21960                 if (
21961                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21962                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21963                     (cc >= 0xf900 && cc < 0xfb00 )
21964                 ) {
21965                         return b;
21966                 }
21967                 return "&#"+cc+";" 
21968             });
21969             if(this.owner.fireEvent('beforesync', this, html) !== false){
21970                 this.el.dom.value = html;
21971                 this.owner.fireEvent('sync', this, html);
21972             }
21973         }
21974     },
21975
21976     /**
21977      * Protected method that will not generally be called directly. Pushes the value of the textarea
21978      * into the iframe editor.
21979      */
21980     pushValue : function(){
21981         if(this.initialized){
21982             var v = this.el.dom.value.trim();
21983             
21984 //            if(v.length < 1){
21985 //                v = '&#160;';
21986 //            }
21987             
21988             if(this.owner.fireEvent('beforepush', this, v) !== false){
21989                 var d = (this.doc.body || this.doc.documentElement);
21990                 d.innerHTML = v;
21991                 this.cleanUpPaste();
21992                 this.el.dom.value = d.innerHTML;
21993                 this.owner.fireEvent('push', this, v);
21994             }
21995         }
21996     },
21997
21998     // private
21999     deferFocus : function(){
22000         this.focus.defer(10, this);
22001     },
22002
22003     // doc'ed in Field
22004     focus : function(){
22005         if(this.win && !this.sourceEditMode){
22006             this.win.focus();
22007         }else{
22008             this.el.focus();
22009         }
22010     },
22011     
22012     assignDocWin: function()
22013     {
22014         var iframe = this.iframe;
22015         
22016          if(Roo.isIE){
22017             this.doc = iframe.contentWindow.document;
22018             this.win = iframe.contentWindow;
22019         } else {
22020 //            if (!Roo.get(this.frameId)) {
22021 //                return;
22022 //            }
22023 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22024 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22025             
22026             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22027                 return;
22028             }
22029             
22030             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22031             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22032         }
22033     },
22034     
22035     // private
22036     initEditor : function(){
22037         //console.log("INIT EDITOR");
22038         this.assignDocWin();
22039         
22040         
22041         
22042         this.doc.designMode="on";
22043         this.doc.open();
22044         this.doc.write(this.getDocMarkup());
22045         this.doc.close();
22046         
22047         var dbody = (this.doc.body || this.doc.documentElement);
22048         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22049         // this copies styles from the containing element into thsi one..
22050         // not sure why we need all of this..
22051         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22052         
22053         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22054         //ss['background-attachment'] = 'fixed'; // w3c
22055         dbody.bgProperties = 'fixed'; // ie
22056         //Roo.DomHelper.applyStyles(dbody, ss);
22057         Roo.EventManager.on(this.doc, {
22058             //'mousedown': this.onEditorEvent,
22059             'mouseup': this.onEditorEvent,
22060             'dblclick': this.onEditorEvent,
22061             'click': this.onEditorEvent,
22062             'keyup': this.onEditorEvent,
22063             buffer:100,
22064             scope: this
22065         });
22066         if(Roo.isGecko){
22067             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22068         }
22069         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22070             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22071         }
22072         this.initialized = true;
22073
22074         this.owner.fireEvent('initialize', this);
22075         this.pushValue();
22076     },
22077
22078     // private
22079     onDestroy : function(){
22080         
22081         
22082         
22083         if(this.rendered){
22084             
22085             //for (var i =0; i < this.toolbars.length;i++) {
22086             //    // fixme - ask toolbars for heights?
22087             //    this.toolbars[i].onDestroy();
22088            // }
22089             
22090             //this.wrap.dom.innerHTML = '';
22091             //this.wrap.remove();
22092         }
22093     },
22094
22095     // private
22096     onFirstFocus : function(){
22097         
22098         this.assignDocWin();
22099         
22100         
22101         this.activated = true;
22102          
22103     
22104         if(Roo.isGecko){ // prevent silly gecko errors
22105             this.win.focus();
22106             var s = this.win.getSelection();
22107             if(!s.focusNode || s.focusNode.nodeType != 3){
22108                 var r = s.getRangeAt(0);
22109                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22110                 r.collapse(true);
22111                 this.deferFocus();
22112             }
22113             try{
22114                 this.execCmd('useCSS', true);
22115                 this.execCmd('styleWithCSS', false);
22116             }catch(e){}
22117         }
22118         this.owner.fireEvent('activate', this);
22119     },
22120
22121     // private
22122     adjustFont: function(btn){
22123         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22124         //if(Roo.isSafari){ // safari
22125         //    adjust *= 2;
22126        // }
22127         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22128         if(Roo.isSafari){ // safari
22129             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22130             v =  (v < 10) ? 10 : v;
22131             v =  (v > 48) ? 48 : v;
22132             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22133             
22134         }
22135         
22136         
22137         v = Math.max(1, v+adjust);
22138         
22139         this.execCmd('FontSize', v  );
22140     },
22141
22142     onEditorEvent : function(e)
22143     {
22144         this.owner.fireEvent('editorevent', this, e);
22145       //  this.updateToolbar();
22146         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22147     },
22148
22149     insertTag : function(tg)
22150     {
22151         // could be a bit smarter... -> wrap the current selected tRoo..
22152         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22153             
22154             range = this.createRange(this.getSelection());
22155             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22156             wrappingNode.appendChild(range.extractContents());
22157             range.insertNode(wrappingNode);
22158
22159             return;
22160             
22161             
22162             
22163         }
22164         this.execCmd("formatblock",   tg);
22165         
22166     },
22167     
22168     insertText : function(txt)
22169     {
22170         
22171         
22172         var range = this.createRange();
22173         range.deleteContents();
22174                //alert(Sender.getAttribute('label'));
22175                
22176         range.insertNode(this.doc.createTextNode(txt));
22177     } ,
22178     
22179      
22180
22181     /**
22182      * Executes a Midas editor command on the editor document and performs necessary focus and
22183      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22184      * @param {String} cmd The Midas command
22185      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22186      */
22187     relayCmd : function(cmd, value){
22188         this.win.focus();
22189         this.execCmd(cmd, value);
22190         this.owner.fireEvent('editorevent', this);
22191         //this.updateToolbar();
22192         this.owner.deferFocus();
22193     },
22194
22195     /**
22196      * Executes a Midas editor command directly on the editor document.
22197      * For visual commands, you should use {@link #relayCmd} instead.
22198      * <b>This should only be called after the editor is initialized.</b>
22199      * @param {String} cmd The Midas command
22200      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22201      */
22202     execCmd : function(cmd, value){
22203         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22204         this.syncValue();
22205     },
22206  
22207  
22208    
22209     /**
22210      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22211      * to insert tRoo.
22212      * @param {String} text | dom node.. 
22213      */
22214     insertAtCursor : function(text)
22215     {
22216         
22217         if(!this.activated){
22218             return;
22219         }
22220         /*
22221         if(Roo.isIE){
22222             this.win.focus();
22223             var r = this.doc.selection.createRange();
22224             if(r){
22225                 r.collapse(true);
22226                 r.pasteHTML(text);
22227                 this.syncValue();
22228                 this.deferFocus();
22229             
22230             }
22231             return;
22232         }
22233         */
22234         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22235             this.win.focus();
22236             
22237             
22238             // from jquery ui (MIT licenced)
22239             var range, node;
22240             var win = this.win;
22241             
22242             if (win.getSelection && win.getSelection().getRangeAt) {
22243                 range = win.getSelection().getRangeAt(0);
22244                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22245                 range.insertNode(node);
22246             } else if (win.document.selection && win.document.selection.createRange) {
22247                 // no firefox support
22248                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22249                 win.document.selection.createRange().pasteHTML(txt);
22250             } else {
22251                 // no firefox support
22252                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22253                 this.execCmd('InsertHTML', txt);
22254             } 
22255             
22256             this.syncValue();
22257             
22258             this.deferFocus();
22259         }
22260     },
22261  // private
22262     mozKeyPress : function(e){
22263         if(e.ctrlKey){
22264             var c = e.getCharCode(), cmd;
22265           
22266             if(c > 0){
22267                 c = String.fromCharCode(c).toLowerCase();
22268                 switch(c){
22269                     case 'b':
22270                         cmd = 'bold';
22271                         break;
22272                     case 'i':
22273                         cmd = 'italic';
22274                         break;
22275                     
22276                     case 'u':
22277                         cmd = 'underline';
22278                         break;
22279                     
22280                     case 'v':
22281                         this.cleanUpPaste.defer(100, this);
22282                         return;
22283                         
22284                 }
22285                 if(cmd){
22286                     this.win.focus();
22287                     this.execCmd(cmd);
22288                     this.deferFocus();
22289                     e.preventDefault();
22290                 }
22291                 
22292             }
22293         }
22294     },
22295
22296     // private
22297     fixKeys : function(){ // load time branching for fastest keydown performance
22298         if(Roo.isIE){
22299             return function(e){
22300                 var k = e.getKey(), r;
22301                 if(k == e.TAB){
22302                     e.stopEvent();
22303                     r = this.doc.selection.createRange();
22304                     if(r){
22305                         r.collapse(true);
22306                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22307                         this.deferFocus();
22308                     }
22309                     return;
22310                 }
22311                 
22312                 if(k == e.ENTER){
22313                     r = this.doc.selection.createRange();
22314                     if(r){
22315                         var target = r.parentElement();
22316                         if(!target || target.tagName.toLowerCase() != 'li'){
22317                             e.stopEvent();
22318                             r.pasteHTML('<br />');
22319                             r.collapse(false);
22320                             r.select();
22321                         }
22322                     }
22323                 }
22324                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22325                     this.cleanUpPaste.defer(100, this);
22326                     return;
22327                 }
22328                 
22329                 
22330             };
22331         }else if(Roo.isOpera){
22332             return function(e){
22333                 var k = e.getKey();
22334                 if(k == e.TAB){
22335                     e.stopEvent();
22336                     this.win.focus();
22337                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22338                     this.deferFocus();
22339                 }
22340                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22341                     this.cleanUpPaste.defer(100, this);
22342                     return;
22343                 }
22344                 
22345             };
22346         }else if(Roo.isSafari){
22347             return function(e){
22348                 var k = e.getKey();
22349                 
22350                 if(k == e.TAB){
22351                     e.stopEvent();
22352                     this.execCmd('InsertText','\t');
22353                     this.deferFocus();
22354                     return;
22355                 }
22356                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22357                     this.cleanUpPaste.defer(100, this);
22358                     return;
22359                 }
22360                 
22361              };
22362         }
22363     }(),
22364     
22365     getAllAncestors: function()
22366     {
22367         var p = this.getSelectedNode();
22368         var a = [];
22369         if (!p) {
22370             a.push(p); // push blank onto stack..
22371             p = this.getParentElement();
22372         }
22373         
22374         
22375         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22376             a.push(p);
22377             p = p.parentNode;
22378         }
22379         a.push(this.doc.body);
22380         return a;
22381     },
22382     lastSel : false,
22383     lastSelNode : false,
22384     
22385     
22386     getSelection : function() 
22387     {
22388         this.assignDocWin();
22389         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22390     },
22391     
22392     getSelectedNode: function() 
22393     {
22394         // this may only work on Gecko!!!
22395         
22396         // should we cache this!!!!
22397         
22398         
22399         
22400          
22401         var range = this.createRange(this.getSelection()).cloneRange();
22402         
22403         if (Roo.isIE) {
22404             var parent = range.parentElement();
22405             while (true) {
22406                 var testRange = range.duplicate();
22407                 testRange.moveToElementText(parent);
22408                 if (testRange.inRange(range)) {
22409                     break;
22410                 }
22411                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22412                     break;
22413                 }
22414                 parent = parent.parentElement;
22415             }
22416             return parent;
22417         }
22418         
22419         // is ancestor a text element.
22420         var ac =  range.commonAncestorContainer;
22421         if (ac.nodeType == 3) {
22422             ac = ac.parentNode;
22423         }
22424         
22425         var ar = ac.childNodes;
22426          
22427         var nodes = [];
22428         var other_nodes = [];
22429         var has_other_nodes = false;
22430         for (var i=0;i<ar.length;i++) {
22431             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22432                 continue;
22433             }
22434             // fullly contained node.
22435             
22436             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22437                 nodes.push(ar[i]);
22438                 continue;
22439             }
22440             
22441             // probably selected..
22442             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22443                 other_nodes.push(ar[i]);
22444                 continue;
22445             }
22446             // outer..
22447             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22448                 continue;
22449             }
22450             
22451             
22452             has_other_nodes = true;
22453         }
22454         if (!nodes.length && other_nodes.length) {
22455             nodes= other_nodes;
22456         }
22457         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22458             return false;
22459         }
22460         
22461         return nodes[0];
22462     },
22463     createRange: function(sel)
22464     {
22465         // this has strange effects when using with 
22466         // top toolbar - not sure if it's a great idea.
22467         //this.editor.contentWindow.focus();
22468         if (typeof sel != "undefined") {
22469             try {
22470                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22471             } catch(e) {
22472                 return this.doc.createRange();
22473             }
22474         } else {
22475             return this.doc.createRange();
22476         }
22477     },
22478     getParentElement: function()
22479     {
22480         
22481         this.assignDocWin();
22482         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22483         
22484         var range = this.createRange(sel);
22485          
22486         try {
22487             var p = range.commonAncestorContainer;
22488             while (p.nodeType == 3) { // text node
22489                 p = p.parentNode;
22490             }
22491             return p;
22492         } catch (e) {
22493             return null;
22494         }
22495     
22496     },
22497     /***
22498      *
22499      * Range intersection.. the hard stuff...
22500      *  '-1' = before
22501      *  '0' = hits..
22502      *  '1' = after.
22503      *         [ -- selected range --- ]
22504      *   [fail]                        [fail]
22505      *
22506      *    basically..
22507      *      if end is before start or  hits it. fail.
22508      *      if start is after end or hits it fail.
22509      *
22510      *   if either hits (but other is outside. - then it's not 
22511      *   
22512      *    
22513      **/
22514     
22515     
22516     // @see http://www.thismuchiknow.co.uk/?p=64.
22517     rangeIntersectsNode : function(range, node)
22518     {
22519         var nodeRange = node.ownerDocument.createRange();
22520         try {
22521             nodeRange.selectNode(node);
22522         } catch (e) {
22523             nodeRange.selectNodeContents(node);
22524         }
22525     
22526         var rangeStartRange = range.cloneRange();
22527         rangeStartRange.collapse(true);
22528     
22529         var rangeEndRange = range.cloneRange();
22530         rangeEndRange.collapse(false);
22531     
22532         var nodeStartRange = nodeRange.cloneRange();
22533         nodeStartRange.collapse(true);
22534     
22535         var nodeEndRange = nodeRange.cloneRange();
22536         nodeEndRange.collapse(false);
22537     
22538         return rangeStartRange.compareBoundaryPoints(
22539                  Range.START_TO_START, nodeEndRange) == -1 &&
22540                rangeEndRange.compareBoundaryPoints(
22541                  Range.START_TO_START, nodeStartRange) == 1;
22542         
22543          
22544     },
22545     rangeCompareNode : function(range, node)
22546     {
22547         var nodeRange = node.ownerDocument.createRange();
22548         try {
22549             nodeRange.selectNode(node);
22550         } catch (e) {
22551             nodeRange.selectNodeContents(node);
22552         }
22553         
22554         
22555         range.collapse(true);
22556     
22557         nodeRange.collapse(true);
22558      
22559         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22560         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22561          
22562         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22563         
22564         var nodeIsBefore   =  ss == 1;
22565         var nodeIsAfter    = ee == -1;
22566         
22567         if (nodeIsBefore && nodeIsAfter) {
22568             return 0; // outer
22569         }
22570         if (!nodeIsBefore && nodeIsAfter) {
22571             return 1; //right trailed.
22572         }
22573         
22574         if (nodeIsBefore && !nodeIsAfter) {
22575             return 2;  // left trailed.
22576         }
22577         // fully contined.
22578         return 3;
22579     },
22580
22581     // private? - in a new class?
22582     cleanUpPaste :  function()
22583     {
22584         // cleans up the whole document..
22585         Roo.log('cleanuppaste');
22586         
22587         this.cleanUpChildren(this.doc.body);
22588         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22589         if (clean != this.doc.body.innerHTML) {
22590             this.doc.body.innerHTML = clean;
22591         }
22592         
22593     },
22594     
22595     cleanWordChars : function(input) {// change the chars to hex code
22596         var he = Roo.HtmlEditorCore;
22597         
22598         var output = input;
22599         Roo.each(he.swapCodes, function(sw) { 
22600             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22601             
22602             output = output.replace(swapper, sw[1]);
22603         });
22604         
22605         return output;
22606     },
22607     
22608     
22609     cleanUpChildren : function (n)
22610     {
22611         if (!n.childNodes.length) {
22612             return;
22613         }
22614         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22615            this.cleanUpChild(n.childNodes[i]);
22616         }
22617     },
22618     
22619     
22620         
22621     
22622     cleanUpChild : function (node)
22623     {
22624         var ed = this;
22625         //console.log(node);
22626         if (node.nodeName == "#text") {
22627             // clean up silly Windows -- stuff?
22628             return; 
22629         }
22630         if (node.nodeName == "#comment") {
22631             node.parentNode.removeChild(node);
22632             // clean up silly Windows -- stuff?
22633             return; 
22634         }
22635         var lcname = node.tagName.toLowerCase();
22636         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22637         // whitelist of tags..
22638         
22639         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22640             // remove node.
22641             node.parentNode.removeChild(node);
22642             return;
22643             
22644         }
22645         
22646         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22647         
22648         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22649         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22650         
22651         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22652         //    remove_keep_children = true;
22653         //}
22654         
22655         if (remove_keep_children) {
22656             this.cleanUpChildren(node);
22657             // inserts everything just before this node...
22658             while (node.childNodes.length) {
22659                 var cn = node.childNodes[0];
22660                 node.removeChild(cn);
22661                 node.parentNode.insertBefore(cn, node);
22662             }
22663             node.parentNode.removeChild(node);
22664             return;
22665         }
22666         
22667         if (!node.attributes || !node.attributes.length) {
22668             this.cleanUpChildren(node);
22669             return;
22670         }
22671         
22672         function cleanAttr(n,v)
22673         {
22674             
22675             if (v.match(/^\./) || v.match(/^\//)) {
22676                 return;
22677             }
22678             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22679                 return;
22680             }
22681             if (v.match(/^#/)) {
22682                 return;
22683             }
22684 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22685             node.removeAttribute(n);
22686             
22687         }
22688         
22689         var cwhite = this.cwhite;
22690         var cblack = this.cblack;
22691             
22692         function cleanStyle(n,v)
22693         {
22694             if (v.match(/expression/)) { //XSS?? should we even bother..
22695                 node.removeAttribute(n);
22696                 return;
22697             }
22698             
22699             var parts = v.split(/;/);
22700             var clean = [];
22701             
22702             Roo.each(parts, function(p) {
22703                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22704                 if (!p.length) {
22705                     return true;
22706                 }
22707                 var l = p.split(':').shift().replace(/\s+/g,'');
22708                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22709                 
22710                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22711 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22712                     //node.removeAttribute(n);
22713                     return true;
22714                 }
22715                 //Roo.log()
22716                 // only allow 'c whitelisted system attributes'
22717                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22718 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22719                     //node.removeAttribute(n);
22720                     return true;
22721                 }
22722                 
22723                 
22724                  
22725                 
22726                 clean.push(p);
22727                 return true;
22728             });
22729             if (clean.length) { 
22730                 node.setAttribute(n, clean.join(';'));
22731             } else {
22732                 node.removeAttribute(n);
22733             }
22734             
22735         }
22736         
22737         
22738         for (var i = node.attributes.length-1; i > -1 ; i--) {
22739             var a = node.attributes[i];
22740             //console.log(a);
22741             
22742             if (a.name.toLowerCase().substr(0,2)=='on')  {
22743                 node.removeAttribute(a.name);
22744                 continue;
22745             }
22746             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22747                 node.removeAttribute(a.name);
22748                 continue;
22749             }
22750             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22751                 cleanAttr(a.name,a.value); // fixme..
22752                 continue;
22753             }
22754             if (a.name == 'style') {
22755                 cleanStyle(a.name,a.value);
22756                 continue;
22757             }
22758             /// clean up MS crap..
22759             // tecnically this should be a list of valid class'es..
22760             
22761             
22762             if (a.name == 'class') {
22763                 if (a.value.match(/^Mso/)) {
22764                     node.className = '';
22765                 }
22766                 
22767                 if (a.value.match(/^body$/)) {
22768                     node.className = '';
22769                 }
22770                 continue;
22771             }
22772             
22773             // style cleanup!?
22774             // class cleanup?
22775             
22776         }
22777         
22778         
22779         this.cleanUpChildren(node);
22780         
22781         
22782     },
22783     
22784     /**
22785      * Clean up MS wordisms...
22786      */
22787     cleanWord : function(node)
22788     {
22789         
22790         
22791         if (!node) {
22792             this.cleanWord(this.doc.body);
22793             return;
22794         }
22795         if (node.nodeName == "#text") {
22796             // clean up silly Windows -- stuff?
22797             return; 
22798         }
22799         if (node.nodeName == "#comment") {
22800             node.parentNode.removeChild(node);
22801             // clean up silly Windows -- stuff?
22802             return; 
22803         }
22804         
22805         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22806             node.parentNode.removeChild(node);
22807             return;
22808         }
22809         
22810         // remove - but keep children..
22811         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22812             while (node.childNodes.length) {
22813                 var cn = node.childNodes[0];
22814                 node.removeChild(cn);
22815                 node.parentNode.insertBefore(cn, node);
22816             }
22817             node.parentNode.removeChild(node);
22818             this.iterateChildren(node, this.cleanWord);
22819             return;
22820         }
22821         // clean styles
22822         if (node.className.length) {
22823             
22824             var cn = node.className.split(/\W+/);
22825             var cna = [];
22826             Roo.each(cn, function(cls) {
22827                 if (cls.match(/Mso[a-zA-Z]+/)) {
22828                     return;
22829                 }
22830                 cna.push(cls);
22831             });
22832             node.className = cna.length ? cna.join(' ') : '';
22833             if (!cna.length) {
22834                 node.removeAttribute("class");
22835             }
22836         }
22837         
22838         if (node.hasAttribute("lang")) {
22839             node.removeAttribute("lang");
22840         }
22841         
22842         if (node.hasAttribute("style")) {
22843             
22844             var styles = node.getAttribute("style").split(";");
22845             var nstyle = [];
22846             Roo.each(styles, function(s) {
22847                 if (!s.match(/:/)) {
22848                     return;
22849                 }
22850                 var kv = s.split(":");
22851                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22852                     return;
22853                 }
22854                 // what ever is left... we allow.
22855                 nstyle.push(s);
22856             });
22857             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22858             if (!nstyle.length) {
22859                 node.removeAttribute('style');
22860             }
22861         }
22862         this.iterateChildren(node, this.cleanWord);
22863         
22864         
22865         
22866     },
22867     /**
22868      * iterateChildren of a Node, calling fn each time, using this as the scole..
22869      * @param {DomNode} node node to iterate children of.
22870      * @param {Function} fn method of this class to call on each item.
22871      */
22872     iterateChildren : function(node, fn)
22873     {
22874         if (!node.childNodes.length) {
22875                 return;
22876         }
22877         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22878            fn.call(this, node.childNodes[i])
22879         }
22880     },
22881     
22882     
22883     /**
22884      * cleanTableWidths.
22885      *
22886      * Quite often pasting from word etc.. results in tables with column and widths.
22887      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22888      *
22889      */
22890     cleanTableWidths : function(node)
22891     {
22892          
22893          
22894         if (!node) {
22895             this.cleanTableWidths(this.doc.body);
22896             return;
22897         }
22898         
22899         // ignore list...
22900         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22901             return; 
22902         }
22903         Roo.log(node.tagName);
22904         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22905             this.iterateChildren(node, this.cleanTableWidths);
22906             return;
22907         }
22908         if (node.hasAttribute('width')) {
22909             node.removeAttribute('width');
22910         }
22911         
22912          
22913         if (node.hasAttribute("style")) {
22914             // pretty basic...
22915             
22916             var styles = node.getAttribute("style").split(";");
22917             var nstyle = [];
22918             Roo.each(styles, function(s) {
22919                 if (!s.match(/:/)) {
22920                     return;
22921                 }
22922                 var kv = s.split(":");
22923                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22924                     return;
22925                 }
22926                 // what ever is left... we allow.
22927                 nstyle.push(s);
22928             });
22929             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22930             if (!nstyle.length) {
22931                 node.removeAttribute('style');
22932             }
22933         }
22934         
22935         this.iterateChildren(node, this.cleanTableWidths);
22936         
22937         
22938     },
22939     
22940     
22941     
22942     
22943     domToHTML : function(currentElement, depth, nopadtext) {
22944         
22945         depth = depth || 0;
22946         nopadtext = nopadtext || false;
22947     
22948         if (!currentElement) {
22949             return this.domToHTML(this.doc.body);
22950         }
22951         
22952         //Roo.log(currentElement);
22953         var j;
22954         var allText = false;
22955         var nodeName = currentElement.nodeName;
22956         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22957         
22958         if  (nodeName == '#text') {
22959             
22960             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22961         }
22962         
22963         
22964         var ret = '';
22965         if (nodeName != 'BODY') {
22966              
22967             var i = 0;
22968             // Prints the node tagName, such as <A>, <IMG>, etc
22969             if (tagName) {
22970                 var attr = [];
22971                 for(i = 0; i < currentElement.attributes.length;i++) {
22972                     // quoting?
22973                     var aname = currentElement.attributes.item(i).name;
22974                     if (!currentElement.attributes.item(i).value.length) {
22975                         continue;
22976                     }
22977                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22978                 }
22979                 
22980                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22981             } 
22982             else {
22983                 
22984                 // eack
22985             }
22986         } else {
22987             tagName = false;
22988         }
22989         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22990             return ret;
22991         }
22992         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22993             nopadtext = true;
22994         }
22995         
22996         
22997         // Traverse the tree
22998         i = 0;
22999         var currentElementChild = currentElement.childNodes.item(i);
23000         var allText = true;
23001         var innerHTML  = '';
23002         lastnode = '';
23003         while (currentElementChild) {
23004             // Formatting code (indent the tree so it looks nice on the screen)
23005             var nopad = nopadtext;
23006             if (lastnode == 'SPAN') {
23007                 nopad  = true;
23008             }
23009             // text
23010             if  (currentElementChild.nodeName == '#text') {
23011                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23012                 toadd = nopadtext ? toadd : toadd.trim();
23013                 if (!nopad && toadd.length > 80) {
23014                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23015                 }
23016                 innerHTML  += toadd;
23017                 
23018                 i++;
23019                 currentElementChild = currentElement.childNodes.item(i);
23020                 lastNode = '';
23021                 continue;
23022             }
23023             allText = false;
23024             
23025             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23026                 
23027             // Recursively traverse the tree structure of the child node
23028             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23029             lastnode = currentElementChild.nodeName;
23030             i++;
23031             currentElementChild=currentElement.childNodes.item(i);
23032         }
23033         
23034         ret += innerHTML;
23035         
23036         if (!allText) {
23037                 // The remaining code is mostly for formatting the tree
23038             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23039         }
23040         
23041         
23042         if (tagName) {
23043             ret+= "</"+tagName+">";
23044         }
23045         return ret;
23046         
23047     },
23048         
23049     applyBlacklists : function()
23050     {
23051         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23052         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23053         
23054         this.white = [];
23055         this.black = [];
23056         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23057             if (b.indexOf(tag) > -1) {
23058                 return;
23059             }
23060             this.white.push(tag);
23061             
23062         }, this);
23063         
23064         Roo.each(w, function(tag) {
23065             if (b.indexOf(tag) > -1) {
23066                 return;
23067             }
23068             if (this.white.indexOf(tag) > -1) {
23069                 return;
23070             }
23071             this.white.push(tag);
23072             
23073         }, this);
23074         
23075         
23076         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23077             if (w.indexOf(tag) > -1) {
23078                 return;
23079             }
23080             this.black.push(tag);
23081             
23082         }, this);
23083         
23084         Roo.each(b, function(tag) {
23085             if (w.indexOf(tag) > -1) {
23086                 return;
23087             }
23088             if (this.black.indexOf(tag) > -1) {
23089                 return;
23090             }
23091             this.black.push(tag);
23092             
23093         }, this);
23094         
23095         
23096         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23097         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23098         
23099         this.cwhite = [];
23100         this.cblack = [];
23101         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23102             if (b.indexOf(tag) > -1) {
23103                 return;
23104             }
23105             this.cwhite.push(tag);
23106             
23107         }, this);
23108         
23109         Roo.each(w, function(tag) {
23110             if (b.indexOf(tag) > -1) {
23111                 return;
23112             }
23113             if (this.cwhite.indexOf(tag) > -1) {
23114                 return;
23115             }
23116             this.cwhite.push(tag);
23117             
23118         }, this);
23119         
23120         
23121         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23122             if (w.indexOf(tag) > -1) {
23123                 return;
23124             }
23125             this.cblack.push(tag);
23126             
23127         }, this);
23128         
23129         Roo.each(b, function(tag) {
23130             if (w.indexOf(tag) > -1) {
23131                 return;
23132             }
23133             if (this.cblack.indexOf(tag) > -1) {
23134                 return;
23135             }
23136             this.cblack.push(tag);
23137             
23138         }, this);
23139     },
23140     
23141     setStylesheets : function(stylesheets)
23142     {
23143         if(typeof(stylesheets) == 'string'){
23144             Roo.get(this.iframe.contentDocument.head).createChild({
23145                 tag : 'link',
23146                 rel : 'stylesheet',
23147                 type : 'text/css',
23148                 href : stylesheets
23149             });
23150             
23151             return;
23152         }
23153         var _this = this;
23154      
23155         Roo.each(stylesheets, function(s) {
23156             if(!s.length){
23157                 return;
23158             }
23159             
23160             Roo.get(_this.iframe.contentDocument.head).createChild({
23161                 tag : 'link',
23162                 rel : 'stylesheet',
23163                 type : 'text/css',
23164                 href : s
23165             });
23166         });
23167
23168         
23169     },
23170     
23171     removeStylesheets : function()
23172     {
23173         var _this = this;
23174         
23175         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23176             s.remove();
23177         });
23178     },
23179     
23180     setStyle : function(style)
23181     {
23182         Roo.get(this.iframe.contentDocument.head).createChild({
23183             tag : 'style',
23184             type : 'text/css',
23185             html : style
23186         });
23187
23188         return;
23189     }
23190     
23191     // hide stuff that is not compatible
23192     /**
23193      * @event blur
23194      * @hide
23195      */
23196     /**
23197      * @event change
23198      * @hide
23199      */
23200     /**
23201      * @event focus
23202      * @hide
23203      */
23204     /**
23205      * @event specialkey
23206      * @hide
23207      */
23208     /**
23209      * @cfg {String} fieldClass @hide
23210      */
23211     /**
23212      * @cfg {String} focusClass @hide
23213      */
23214     /**
23215      * @cfg {String} autoCreate @hide
23216      */
23217     /**
23218      * @cfg {String} inputType @hide
23219      */
23220     /**
23221      * @cfg {String} invalidClass @hide
23222      */
23223     /**
23224      * @cfg {String} invalidText @hide
23225      */
23226     /**
23227      * @cfg {String} msgFx @hide
23228      */
23229     /**
23230      * @cfg {String} validateOnBlur @hide
23231      */
23232 });
23233
23234 Roo.HtmlEditorCore.white = [
23235         'area', 'br', 'img', 'input', 'hr', 'wbr',
23236         
23237        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23238        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23239        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23240        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23241        'table',   'ul',         'xmp', 
23242        
23243        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23244       'thead',   'tr', 
23245      
23246       'dir', 'menu', 'ol', 'ul', 'dl',
23247        
23248       'embed',  'object'
23249 ];
23250
23251
23252 Roo.HtmlEditorCore.black = [
23253     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23254         'applet', // 
23255         'base',   'basefont', 'bgsound', 'blink',  'body', 
23256         'frame',  'frameset', 'head',    'html',   'ilayer', 
23257         'iframe', 'layer',  'link',     'meta',    'object',   
23258         'script', 'style' ,'title',  'xml' // clean later..
23259 ];
23260 Roo.HtmlEditorCore.clean = [
23261     'script', 'style', 'title', 'xml'
23262 ];
23263 Roo.HtmlEditorCore.remove = [
23264     'font'
23265 ];
23266 // attributes..
23267
23268 Roo.HtmlEditorCore.ablack = [
23269     'on'
23270 ];
23271     
23272 Roo.HtmlEditorCore.aclean = [ 
23273     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23274 ];
23275
23276 // protocols..
23277 Roo.HtmlEditorCore.pwhite= [
23278         'http',  'https',  'mailto'
23279 ];
23280
23281 // white listed style attributes.
23282 Roo.HtmlEditorCore.cwhite= [
23283       //  'text-align', /// default is to allow most things..
23284       
23285          
23286 //        'font-size'//??
23287 ];
23288
23289 // black listed style attributes.
23290 Roo.HtmlEditorCore.cblack= [
23291       //  'font-size' -- this can be set by the project 
23292 ];
23293
23294
23295 Roo.HtmlEditorCore.swapCodes   =[ 
23296     [    8211, "--" ], 
23297     [    8212, "--" ], 
23298     [    8216,  "'" ],  
23299     [    8217, "'" ],  
23300     [    8220, '"' ],  
23301     [    8221, '"' ],  
23302     [    8226, "*" ],  
23303     [    8230, "..." ]
23304 ]; 
23305
23306     /*
23307  * - LGPL
23308  *
23309  * HtmlEditor
23310  * 
23311  */
23312
23313 /**
23314  * @class Roo.bootstrap.HtmlEditor
23315  * @extends Roo.bootstrap.TextArea
23316  * Bootstrap HtmlEditor class
23317
23318  * @constructor
23319  * Create a new HtmlEditor
23320  * @param {Object} config The config object
23321  */
23322
23323 Roo.bootstrap.HtmlEditor = function(config){
23324     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23325     if (!this.toolbars) {
23326         this.toolbars = [];
23327     }
23328     
23329     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23330     this.addEvents({
23331             /**
23332              * @event initialize
23333              * Fires when the editor is fully initialized (including the iframe)
23334              * @param {HtmlEditor} this
23335              */
23336             initialize: true,
23337             /**
23338              * @event activate
23339              * Fires when the editor is first receives the focus. Any insertion must wait
23340              * until after this event.
23341              * @param {HtmlEditor} this
23342              */
23343             activate: true,
23344              /**
23345              * @event beforesync
23346              * Fires before the textarea is updated with content from the editor iframe. Return false
23347              * to cancel the sync.
23348              * @param {HtmlEditor} this
23349              * @param {String} html
23350              */
23351             beforesync: true,
23352              /**
23353              * @event beforepush
23354              * Fires before the iframe editor is updated with content from the textarea. Return false
23355              * to cancel the push.
23356              * @param {HtmlEditor} this
23357              * @param {String} html
23358              */
23359             beforepush: true,
23360              /**
23361              * @event sync
23362              * Fires when the textarea is updated with content from the editor iframe.
23363              * @param {HtmlEditor} this
23364              * @param {String} html
23365              */
23366             sync: true,
23367              /**
23368              * @event push
23369              * Fires when the iframe editor is updated with content from the textarea.
23370              * @param {HtmlEditor} this
23371              * @param {String} html
23372              */
23373             push: true,
23374              /**
23375              * @event editmodechange
23376              * Fires when the editor switches edit modes
23377              * @param {HtmlEditor} this
23378              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23379              */
23380             editmodechange: true,
23381             /**
23382              * @event editorevent
23383              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23384              * @param {HtmlEditor} this
23385              */
23386             editorevent: true,
23387             /**
23388              * @event firstfocus
23389              * Fires when on first focus - needed by toolbars..
23390              * @param {HtmlEditor} this
23391              */
23392             firstfocus: true,
23393             /**
23394              * @event autosave
23395              * Auto save the htmlEditor value as a file into Events
23396              * @param {HtmlEditor} this
23397              */
23398             autosave: true,
23399             /**
23400              * @event savedpreview
23401              * preview the saved version of htmlEditor
23402              * @param {HtmlEditor} this
23403              */
23404             savedpreview: true
23405         });
23406 };
23407
23408
23409 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23410     
23411     
23412       /**
23413      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23414      */
23415     toolbars : false,
23416     
23417      /**
23418     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23419     */
23420     btns : [],
23421    
23422      /**
23423      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23424      *                        Roo.resizable.
23425      */
23426     resizable : false,
23427      /**
23428      * @cfg {Number} height (in pixels)
23429      */   
23430     height: 300,
23431    /**
23432      * @cfg {Number} width (in pixels)
23433      */   
23434     width: false,
23435     
23436     /**
23437      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23438      * 
23439      */
23440     stylesheets: false,
23441     
23442     // id of frame..
23443     frameId: false,
23444     
23445     // private properties
23446     validationEvent : false,
23447     deferHeight: true,
23448     initialized : false,
23449     activated : false,
23450     
23451     onFocus : Roo.emptyFn,
23452     iframePad:3,
23453     hideMode:'offsets',
23454     
23455     tbContainer : false,
23456     
23457     bodyCls : '',
23458     
23459     toolbarContainer :function() {
23460         return this.wrap.select('.x-html-editor-tb',true).first();
23461     },
23462
23463     /**
23464      * Protected method that will not generally be called directly. It
23465      * is called when the editor creates its toolbar. Override this method if you need to
23466      * add custom toolbar buttons.
23467      * @param {HtmlEditor} editor
23468      */
23469     createToolbar : function(){
23470         Roo.log('renewing');
23471         Roo.log("create toolbars");
23472         
23473         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23474         this.toolbars[0].render(this.toolbarContainer());
23475         
23476         return;
23477         
23478 //        if (!editor.toolbars || !editor.toolbars.length) {
23479 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23480 //        }
23481 //        
23482 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23483 //            editor.toolbars[i] = Roo.factory(
23484 //                    typeof(editor.toolbars[i]) == 'string' ?
23485 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23486 //                Roo.bootstrap.HtmlEditor);
23487 //            editor.toolbars[i].init(editor);
23488 //        }
23489     },
23490
23491      
23492     // private
23493     onRender : function(ct, position)
23494     {
23495        // Roo.log("Call onRender: " + this.xtype);
23496         var _t = this;
23497         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23498       
23499         this.wrap = this.inputEl().wrap({
23500             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23501         });
23502         
23503         this.editorcore.onRender(ct, position);
23504          
23505         if (this.resizable) {
23506             this.resizeEl = new Roo.Resizable(this.wrap, {
23507                 pinned : true,
23508                 wrap: true,
23509                 dynamic : true,
23510                 minHeight : this.height,
23511                 height: this.height,
23512                 handles : this.resizable,
23513                 width: this.width,
23514                 listeners : {
23515                     resize : function(r, w, h) {
23516                         _t.onResize(w,h); // -something
23517                     }
23518                 }
23519             });
23520             
23521         }
23522         this.createToolbar(this);
23523        
23524         
23525         if(!this.width && this.resizable){
23526             this.setSize(this.wrap.getSize());
23527         }
23528         if (this.resizeEl) {
23529             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23530             // should trigger onReize..
23531         }
23532         
23533     },
23534
23535     // private
23536     onResize : function(w, h)
23537     {
23538         Roo.log('resize: ' +w + ',' + h );
23539         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23540         var ew = false;
23541         var eh = false;
23542         
23543         if(this.inputEl() ){
23544             if(typeof w == 'number'){
23545                 var aw = w - this.wrap.getFrameWidth('lr');
23546                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23547                 ew = aw;
23548             }
23549             if(typeof h == 'number'){
23550                  var tbh = -11;  // fixme it needs to tool bar size!
23551                 for (var i =0; i < this.toolbars.length;i++) {
23552                     // fixme - ask toolbars for heights?
23553                     tbh += this.toolbars[i].el.getHeight();
23554                     //if (this.toolbars[i].footer) {
23555                     //    tbh += this.toolbars[i].footer.el.getHeight();
23556                     //}
23557                 }
23558               
23559                 
23560                 
23561                 
23562                 
23563                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23564                 ah -= 5; // knock a few pixes off for look..
23565                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23566                 var eh = ah;
23567             }
23568         }
23569         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23570         this.editorcore.onResize(ew,eh);
23571         
23572     },
23573
23574     /**
23575      * Toggles the editor between standard and source edit mode.
23576      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23577      */
23578     toggleSourceEdit : function(sourceEditMode)
23579     {
23580         this.editorcore.toggleSourceEdit(sourceEditMode);
23581         
23582         if(this.editorcore.sourceEditMode){
23583             Roo.log('editor - showing textarea');
23584             
23585 //            Roo.log('in');
23586 //            Roo.log(this.syncValue());
23587             this.syncValue();
23588             this.inputEl().removeClass(['hide', 'x-hidden']);
23589             this.inputEl().dom.removeAttribute('tabIndex');
23590             this.inputEl().focus();
23591         }else{
23592             Roo.log('editor - hiding textarea');
23593 //            Roo.log('out')
23594 //            Roo.log(this.pushValue()); 
23595             this.pushValue();
23596             
23597             this.inputEl().addClass(['hide', 'x-hidden']);
23598             this.inputEl().dom.setAttribute('tabIndex', -1);
23599             //this.deferFocus();
23600         }
23601          
23602         if(this.resizable){
23603             this.setSize(this.wrap.getSize());
23604         }
23605         
23606         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23607     },
23608  
23609     // private (for BoxComponent)
23610     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23611
23612     // private (for BoxComponent)
23613     getResizeEl : function(){
23614         return this.wrap;
23615     },
23616
23617     // private (for BoxComponent)
23618     getPositionEl : function(){
23619         return this.wrap;
23620     },
23621
23622     // private
23623     initEvents : function(){
23624         this.originalValue = this.getValue();
23625     },
23626
23627 //    /**
23628 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23629 //     * @method
23630 //     */
23631 //    markInvalid : Roo.emptyFn,
23632 //    /**
23633 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23634 //     * @method
23635 //     */
23636 //    clearInvalid : Roo.emptyFn,
23637
23638     setValue : function(v){
23639         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23640         this.editorcore.pushValue();
23641     },
23642
23643      
23644     // private
23645     deferFocus : function(){
23646         this.focus.defer(10, this);
23647     },
23648
23649     // doc'ed in Field
23650     focus : function(){
23651         this.editorcore.focus();
23652         
23653     },
23654       
23655
23656     // private
23657     onDestroy : function(){
23658         
23659         
23660         
23661         if(this.rendered){
23662             
23663             for (var i =0; i < this.toolbars.length;i++) {
23664                 // fixme - ask toolbars for heights?
23665                 this.toolbars[i].onDestroy();
23666             }
23667             
23668             this.wrap.dom.innerHTML = '';
23669             this.wrap.remove();
23670         }
23671     },
23672
23673     // private
23674     onFirstFocus : function(){
23675         //Roo.log("onFirstFocus");
23676         this.editorcore.onFirstFocus();
23677          for (var i =0; i < this.toolbars.length;i++) {
23678             this.toolbars[i].onFirstFocus();
23679         }
23680         
23681     },
23682     
23683     // private
23684     syncValue : function()
23685     {   
23686         this.editorcore.syncValue();
23687     },
23688     
23689     pushValue : function()
23690     {   
23691         this.editorcore.pushValue();
23692     }
23693      
23694     
23695     // hide stuff that is not compatible
23696     /**
23697      * @event blur
23698      * @hide
23699      */
23700     /**
23701      * @event change
23702      * @hide
23703      */
23704     /**
23705      * @event focus
23706      * @hide
23707      */
23708     /**
23709      * @event specialkey
23710      * @hide
23711      */
23712     /**
23713      * @cfg {String} fieldClass @hide
23714      */
23715     /**
23716      * @cfg {String} focusClass @hide
23717      */
23718     /**
23719      * @cfg {String} autoCreate @hide
23720      */
23721     /**
23722      * @cfg {String} inputType @hide
23723      */
23724     /**
23725      * @cfg {String} invalidClass @hide
23726      */
23727     /**
23728      * @cfg {String} invalidText @hide
23729      */
23730     /**
23731      * @cfg {String} msgFx @hide
23732      */
23733     /**
23734      * @cfg {String} validateOnBlur @hide
23735      */
23736 });
23737  
23738     
23739    
23740    
23741    
23742       
23743 Roo.namespace('Roo.bootstrap.htmleditor');
23744 /**
23745  * @class Roo.bootstrap.HtmlEditorToolbar1
23746  * Basic Toolbar
23747  * 
23748  * Usage:
23749  *
23750  new Roo.bootstrap.HtmlEditor({
23751     ....
23752     toolbars : [
23753         new Roo.bootstrap.HtmlEditorToolbar1({
23754             disable : { fonts: 1 , format: 1, ..., ... , ...],
23755             btns : [ .... ]
23756         })
23757     }
23758      
23759  * 
23760  * @cfg {Object} disable List of elements to disable..
23761  * @cfg {Array} btns List of additional buttons.
23762  * 
23763  * 
23764  * NEEDS Extra CSS? 
23765  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23766  */
23767  
23768 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23769 {
23770     
23771     Roo.apply(this, config);
23772     
23773     // default disabled, based on 'good practice'..
23774     this.disable = this.disable || {};
23775     Roo.applyIf(this.disable, {
23776         fontSize : true,
23777         colors : true,
23778         specialElements : true
23779     });
23780     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23781     
23782     this.editor = config.editor;
23783     this.editorcore = config.editor.editorcore;
23784     
23785     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23786     
23787     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23788     // dont call parent... till later.
23789 }
23790 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23791      
23792     bar : true,
23793     
23794     editor : false,
23795     editorcore : false,
23796     
23797     
23798     formats : [
23799         "p" ,  
23800         "h1","h2","h3","h4","h5","h6", 
23801         "pre", "code", 
23802         "abbr", "acronym", "address", "cite", "samp", "var",
23803         'div','span'
23804     ],
23805     
23806     onRender : function(ct, position)
23807     {
23808        // Roo.log("Call onRender: " + this.xtype);
23809         
23810        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23811        Roo.log(this.el);
23812        this.el.dom.style.marginBottom = '0';
23813        var _this = this;
23814        var editorcore = this.editorcore;
23815        var editor= this.editor;
23816        
23817        var children = [];
23818        var btn = function(id,cmd , toggle, handler, html){
23819        
23820             var  event = toggle ? 'toggle' : 'click';
23821        
23822             var a = {
23823                 size : 'sm',
23824                 xtype: 'Button',
23825                 xns: Roo.bootstrap,
23826                 glyphicon : id,
23827                 cmd : id || cmd,
23828                 enableToggle:toggle !== false,
23829                 html : html || '',
23830                 pressed : toggle ? false : null,
23831                 listeners : {}
23832             };
23833             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23834                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23835             };
23836             children.push(a);
23837             return a;
23838        }
23839        
23840     //    var cb_box = function...
23841         
23842         var style = {
23843                 xtype: 'Button',
23844                 size : 'sm',
23845                 xns: Roo.bootstrap,
23846                 glyphicon : 'font',
23847                 //html : 'submit'
23848                 menu : {
23849                     xtype: 'Menu',
23850                     xns: Roo.bootstrap,
23851                     items:  []
23852                 }
23853         };
23854         Roo.each(this.formats, function(f) {
23855             style.menu.items.push({
23856                 xtype :'MenuItem',
23857                 xns: Roo.bootstrap,
23858                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23859                 tagname : f,
23860                 listeners : {
23861                     click : function()
23862                     {
23863                         editorcore.insertTag(this.tagname);
23864                         editor.focus();
23865                     }
23866                 }
23867                 
23868             });
23869         });
23870         children.push(style);   
23871         
23872         btn('bold',false,true);
23873         btn('italic',false,true);
23874         btn('align-left', 'justifyleft',true);
23875         btn('align-center', 'justifycenter',true);
23876         btn('align-right' , 'justifyright',true);
23877         btn('link', false, false, function(btn) {
23878             //Roo.log("create link?");
23879             var url = prompt(this.createLinkText, this.defaultLinkValue);
23880             if(url && url != 'http:/'+'/'){
23881                 this.editorcore.relayCmd('createlink', url);
23882             }
23883         }),
23884         btn('list','insertunorderedlist',true);
23885         btn('pencil', false,true, function(btn){
23886                 Roo.log(this);
23887                 this.toggleSourceEdit(btn.pressed);
23888         });
23889         
23890         if (this.editor.btns.length > 0) {
23891             for (var i = 0; i<this.editor.btns.length; i++) {
23892                 children.push(this.editor.btns[i]);
23893             }
23894         }
23895         
23896         /*
23897         var cog = {
23898                 xtype: 'Button',
23899                 size : 'sm',
23900                 xns: Roo.bootstrap,
23901                 glyphicon : 'cog',
23902                 //html : 'submit'
23903                 menu : {
23904                     xtype: 'Menu',
23905                     xns: Roo.bootstrap,
23906                     items:  []
23907                 }
23908         };
23909         
23910         cog.menu.items.push({
23911             xtype :'MenuItem',
23912             xns: Roo.bootstrap,
23913             html : Clean styles,
23914             tagname : f,
23915             listeners : {
23916                 click : function()
23917                 {
23918                     editorcore.insertTag(this.tagname);
23919                     editor.focus();
23920                 }
23921             }
23922             
23923         });
23924        */
23925         
23926          
23927        this.xtype = 'NavSimplebar';
23928         
23929         for(var i=0;i< children.length;i++) {
23930             
23931             this.buttons.add(this.addxtypeChild(children[i]));
23932             
23933         }
23934         
23935         editor.on('editorevent', this.updateToolbar, this);
23936     },
23937     onBtnClick : function(id)
23938     {
23939        this.editorcore.relayCmd(id);
23940        this.editorcore.focus();
23941     },
23942     
23943     /**
23944      * Protected method that will not generally be called directly. It triggers
23945      * a toolbar update by reading the markup state of the current selection in the editor.
23946      */
23947     updateToolbar: function(){
23948
23949         if(!this.editorcore.activated){
23950             this.editor.onFirstFocus(); // is this neeed?
23951             return;
23952         }
23953
23954         var btns = this.buttons; 
23955         var doc = this.editorcore.doc;
23956         btns.get('bold').setActive(doc.queryCommandState('bold'));
23957         btns.get('italic').setActive(doc.queryCommandState('italic'));
23958         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23959         
23960         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23961         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23962         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23963         
23964         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23965         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23966          /*
23967         
23968         var ans = this.editorcore.getAllAncestors();
23969         if (this.formatCombo) {
23970             
23971             
23972             var store = this.formatCombo.store;
23973             this.formatCombo.setValue("");
23974             for (var i =0; i < ans.length;i++) {
23975                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23976                     // select it..
23977                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23978                     break;
23979                 }
23980             }
23981         }
23982         
23983         
23984         
23985         // hides menus... - so this cant be on a menu...
23986         Roo.bootstrap.MenuMgr.hideAll();
23987         */
23988         Roo.bootstrap.MenuMgr.hideAll();
23989         //this.editorsyncValue();
23990     },
23991     onFirstFocus: function() {
23992         this.buttons.each(function(item){
23993            item.enable();
23994         });
23995     },
23996     toggleSourceEdit : function(sourceEditMode){
23997         
23998           
23999         if(sourceEditMode){
24000             Roo.log("disabling buttons");
24001            this.buttons.each( function(item){
24002                 if(item.cmd != 'pencil'){
24003                     item.disable();
24004                 }
24005             });
24006           
24007         }else{
24008             Roo.log("enabling buttons");
24009             if(this.editorcore.initialized){
24010                 this.buttons.each( function(item){
24011                     item.enable();
24012                 });
24013             }
24014             
24015         }
24016         Roo.log("calling toggole on editor");
24017         // tell the editor that it's been pressed..
24018         this.editor.toggleSourceEdit(sourceEditMode);
24019        
24020     }
24021 });
24022
24023
24024
24025
24026
24027 /**
24028  * @class Roo.bootstrap.Table.AbstractSelectionModel
24029  * @extends Roo.util.Observable
24030  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24031  * implemented by descendant classes.  This class should not be directly instantiated.
24032  * @constructor
24033  */
24034 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24035     this.locked = false;
24036     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24037 };
24038
24039
24040 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24041     /** @ignore Called by the grid automatically. Do not call directly. */
24042     init : function(grid){
24043         this.grid = grid;
24044         this.initEvents();
24045     },
24046
24047     /**
24048      * Locks the selections.
24049      */
24050     lock : function(){
24051         this.locked = true;
24052     },
24053
24054     /**
24055      * Unlocks the selections.
24056      */
24057     unlock : function(){
24058         this.locked = false;
24059     },
24060
24061     /**
24062      * Returns true if the selections are locked.
24063      * @return {Boolean}
24064      */
24065     isLocked : function(){
24066         return this.locked;
24067     }
24068 });
24069 /**
24070  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24071  * @class Roo.bootstrap.Table.RowSelectionModel
24072  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24073  * It supports multiple selections and keyboard selection/navigation. 
24074  * @constructor
24075  * @param {Object} config
24076  */
24077
24078 Roo.bootstrap.Table.RowSelectionModel = function(config){
24079     Roo.apply(this, config);
24080     this.selections = new Roo.util.MixedCollection(false, function(o){
24081         return o.id;
24082     });
24083
24084     this.last = false;
24085     this.lastActive = false;
24086
24087     this.addEvents({
24088         /**
24089              * @event selectionchange
24090              * Fires when the selection changes
24091              * @param {SelectionModel} this
24092              */
24093             "selectionchange" : true,
24094         /**
24095              * @event afterselectionchange
24096              * Fires after the selection changes (eg. by key press or clicking)
24097              * @param {SelectionModel} this
24098              */
24099             "afterselectionchange" : true,
24100         /**
24101              * @event beforerowselect
24102              * Fires when a row is selected being selected, return false to cancel.
24103              * @param {SelectionModel} this
24104              * @param {Number} rowIndex The selected index
24105              * @param {Boolean} keepExisting False if other selections will be cleared
24106              */
24107             "beforerowselect" : true,
24108         /**
24109              * @event rowselect
24110              * Fires when a row is selected.
24111              * @param {SelectionModel} this
24112              * @param {Number} rowIndex The selected index
24113              * @param {Roo.data.Record} r The record
24114              */
24115             "rowselect" : true,
24116         /**
24117              * @event rowdeselect
24118              * Fires when a row is deselected.
24119              * @param {SelectionModel} this
24120              * @param {Number} rowIndex The selected index
24121              */
24122         "rowdeselect" : true
24123     });
24124     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24125     this.locked = false;
24126  };
24127
24128 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24129     /**
24130      * @cfg {Boolean} singleSelect
24131      * True to allow selection of only one row at a time (defaults to false)
24132      */
24133     singleSelect : false,
24134
24135     // private
24136     initEvents : function()
24137     {
24138
24139         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24140         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24141         //}else{ // allow click to work like normal
24142          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24143         //}
24144         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24145         this.grid.on("rowclick", this.handleMouseDown, this);
24146         
24147         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24148             "up" : function(e){
24149                 if(!e.shiftKey){
24150                     this.selectPrevious(e.shiftKey);
24151                 }else if(this.last !== false && this.lastActive !== false){
24152                     var last = this.last;
24153                     this.selectRange(this.last,  this.lastActive-1);
24154                     this.grid.getView().focusRow(this.lastActive);
24155                     if(last !== false){
24156                         this.last = last;
24157                     }
24158                 }else{
24159                     this.selectFirstRow();
24160                 }
24161                 this.fireEvent("afterselectionchange", this);
24162             },
24163             "down" : function(e){
24164                 if(!e.shiftKey){
24165                     this.selectNext(e.shiftKey);
24166                 }else if(this.last !== false && this.lastActive !== false){
24167                     var last = this.last;
24168                     this.selectRange(this.last,  this.lastActive+1);
24169                     this.grid.getView().focusRow(this.lastActive);
24170                     if(last !== false){
24171                         this.last = last;
24172                     }
24173                 }else{
24174                     this.selectFirstRow();
24175                 }
24176                 this.fireEvent("afterselectionchange", this);
24177             },
24178             scope: this
24179         });
24180         this.grid.store.on('load', function(){
24181             this.selections.clear();
24182         },this);
24183         /*
24184         var view = this.grid.view;
24185         view.on("refresh", this.onRefresh, this);
24186         view.on("rowupdated", this.onRowUpdated, this);
24187         view.on("rowremoved", this.onRemove, this);
24188         */
24189     },
24190
24191     // private
24192     onRefresh : function()
24193     {
24194         var ds = this.grid.store, i, v = this.grid.view;
24195         var s = this.selections;
24196         s.each(function(r){
24197             if((i = ds.indexOfId(r.id)) != -1){
24198                 v.onRowSelect(i);
24199             }else{
24200                 s.remove(r);
24201             }
24202         });
24203     },
24204
24205     // private
24206     onRemove : function(v, index, r){
24207         this.selections.remove(r);
24208     },
24209
24210     // private
24211     onRowUpdated : function(v, index, r){
24212         if(this.isSelected(r)){
24213             v.onRowSelect(index);
24214         }
24215     },
24216
24217     /**
24218      * Select records.
24219      * @param {Array} records The records to select
24220      * @param {Boolean} keepExisting (optional) True to keep existing selections
24221      */
24222     selectRecords : function(records, keepExisting)
24223     {
24224         if(!keepExisting){
24225             this.clearSelections();
24226         }
24227             var ds = this.grid.store;
24228         for(var i = 0, len = records.length; i < len; i++){
24229             this.selectRow(ds.indexOf(records[i]), true);
24230         }
24231     },
24232
24233     /**
24234      * Gets the number of selected rows.
24235      * @return {Number}
24236      */
24237     getCount : function(){
24238         return this.selections.length;
24239     },
24240
24241     /**
24242      * Selects the first row in the grid.
24243      */
24244     selectFirstRow : function(){
24245         this.selectRow(0);
24246     },
24247
24248     /**
24249      * Select the last row.
24250      * @param {Boolean} keepExisting (optional) True to keep existing selections
24251      */
24252     selectLastRow : function(keepExisting){
24253         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24254         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24255     },
24256
24257     /**
24258      * Selects the row immediately following the last selected row.
24259      * @param {Boolean} keepExisting (optional) True to keep existing selections
24260      */
24261     selectNext : function(keepExisting)
24262     {
24263             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24264             this.selectRow(this.last+1, keepExisting);
24265             this.grid.getView().focusRow(this.last);
24266         }
24267     },
24268
24269     /**
24270      * Selects the row that precedes the last selected row.
24271      * @param {Boolean} keepExisting (optional) True to keep existing selections
24272      */
24273     selectPrevious : function(keepExisting){
24274         if(this.last){
24275             this.selectRow(this.last-1, keepExisting);
24276             this.grid.getView().focusRow(this.last);
24277         }
24278     },
24279
24280     /**
24281      * Returns the selected records
24282      * @return {Array} Array of selected records
24283      */
24284     getSelections : function(){
24285         return [].concat(this.selections.items);
24286     },
24287
24288     /**
24289      * Returns the first selected record.
24290      * @return {Record}
24291      */
24292     getSelected : function(){
24293         return this.selections.itemAt(0);
24294     },
24295
24296
24297     /**
24298      * Clears all selections.
24299      */
24300     clearSelections : function(fast)
24301     {
24302         if(this.locked) {
24303             return;
24304         }
24305         if(fast !== true){
24306                 var ds = this.grid.store;
24307             var s = this.selections;
24308             s.each(function(r){
24309                 this.deselectRow(ds.indexOfId(r.id));
24310             }, this);
24311             s.clear();
24312         }else{
24313             this.selections.clear();
24314         }
24315         this.last = false;
24316     },
24317
24318
24319     /**
24320      * Selects all rows.
24321      */
24322     selectAll : function(){
24323         if(this.locked) {
24324             return;
24325         }
24326         this.selections.clear();
24327         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24328             this.selectRow(i, true);
24329         }
24330     },
24331
24332     /**
24333      * Returns True if there is a selection.
24334      * @return {Boolean}
24335      */
24336     hasSelection : function(){
24337         return this.selections.length > 0;
24338     },
24339
24340     /**
24341      * Returns True if the specified row is selected.
24342      * @param {Number/Record} record The record or index of the record to check
24343      * @return {Boolean}
24344      */
24345     isSelected : function(index){
24346             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24347         return (r && this.selections.key(r.id) ? true : false);
24348     },
24349
24350     /**
24351      * Returns True if the specified record id is selected.
24352      * @param {String} id The id of record to check
24353      * @return {Boolean}
24354      */
24355     isIdSelected : function(id){
24356         return (this.selections.key(id) ? true : false);
24357     },
24358
24359
24360     // private
24361     handleMouseDBClick : function(e, t){
24362         
24363     },
24364     // private
24365     handleMouseDown : function(e, t)
24366     {
24367             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24368         if(this.isLocked() || rowIndex < 0 ){
24369             return;
24370         };
24371         if(e.shiftKey && this.last !== false){
24372             var last = this.last;
24373             this.selectRange(last, rowIndex, e.ctrlKey);
24374             this.last = last; // reset the last
24375             t.focus();
24376     
24377         }else{
24378             var isSelected = this.isSelected(rowIndex);
24379             //Roo.log("select row:" + rowIndex);
24380             if(isSelected){
24381                 this.deselectRow(rowIndex);
24382             } else {
24383                         this.selectRow(rowIndex, true);
24384             }
24385     
24386             /*
24387                 if(e.button !== 0 && isSelected){
24388                 alert('rowIndex 2: ' + rowIndex);
24389                     view.focusRow(rowIndex);
24390                 }else if(e.ctrlKey && isSelected){
24391                     this.deselectRow(rowIndex);
24392                 }else if(!isSelected){
24393                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24394                     view.focusRow(rowIndex);
24395                 }
24396             */
24397         }
24398         this.fireEvent("afterselectionchange", this);
24399     },
24400     // private
24401     handleDragableRowClick :  function(grid, rowIndex, e) 
24402     {
24403         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24404             this.selectRow(rowIndex, false);
24405             grid.view.focusRow(rowIndex);
24406              this.fireEvent("afterselectionchange", this);
24407         }
24408     },
24409     
24410     /**
24411      * Selects multiple rows.
24412      * @param {Array} rows Array of the indexes of the row to select
24413      * @param {Boolean} keepExisting (optional) True to keep existing selections
24414      */
24415     selectRows : function(rows, keepExisting){
24416         if(!keepExisting){
24417             this.clearSelections();
24418         }
24419         for(var i = 0, len = rows.length; i < len; i++){
24420             this.selectRow(rows[i], true);
24421         }
24422     },
24423
24424     /**
24425      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24426      * @param {Number} startRow The index of the first row in the range
24427      * @param {Number} endRow The index of the last row in the range
24428      * @param {Boolean} keepExisting (optional) True to retain existing selections
24429      */
24430     selectRange : function(startRow, endRow, keepExisting){
24431         if(this.locked) {
24432             return;
24433         }
24434         if(!keepExisting){
24435             this.clearSelections();
24436         }
24437         if(startRow <= endRow){
24438             for(var i = startRow; i <= endRow; i++){
24439                 this.selectRow(i, true);
24440             }
24441         }else{
24442             for(var i = startRow; i >= endRow; i--){
24443                 this.selectRow(i, true);
24444             }
24445         }
24446     },
24447
24448     /**
24449      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
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      */
24453     deselectRange : function(startRow, endRow, preventViewNotify){
24454         if(this.locked) {
24455             return;
24456         }
24457         for(var i = startRow; i <= endRow; i++){
24458             this.deselectRow(i, preventViewNotify);
24459         }
24460     },
24461
24462     /**
24463      * Selects a row.
24464      * @param {Number} row The index of the row to select
24465      * @param {Boolean} keepExisting (optional) True to keep existing selections
24466      */
24467     selectRow : function(index, keepExisting, preventViewNotify)
24468     {
24469             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24470             return;
24471         }
24472         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24473             if(!keepExisting || this.singleSelect){
24474                 this.clearSelections();
24475             }
24476             
24477             var r = this.grid.store.getAt(index);
24478             //console.log('selectRow - record id :' + r.id);
24479             
24480             this.selections.add(r);
24481             this.last = this.lastActive = index;
24482             if(!preventViewNotify){
24483                 var proxy = new Roo.Element(
24484                                 this.grid.getRowDom(index)
24485                 );
24486                 proxy.addClass('bg-info info');
24487             }
24488             this.fireEvent("rowselect", this, index, r);
24489             this.fireEvent("selectionchange", this);
24490         }
24491     },
24492
24493     /**
24494      * Deselects a row.
24495      * @param {Number} row The index of the row to deselect
24496      */
24497     deselectRow : function(index, preventViewNotify)
24498     {
24499         if(this.locked) {
24500             return;
24501         }
24502         if(this.last == index){
24503             this.last = false;
24504         }
24505         if(this.lastActive == index){
24506             this.lastActive = false;
24507         }
24508         
24509         var r = this.grid.store.getAt(index);
24510         if (!r) {
24511             return;
24512         }
24513         
24514         this.selections.remove(r);
24515         //.console.log('deselectRow - record id :' + r.id);
24516         if(!preventViewNotify){
24517         
24518             var proxy = new Roo.Element(
24519                 this.grid.getRowDom(index)
24520             );
24521             proxy.removeClass('bg-info info');
24522         }
24523         this.fireEvent("rowdeselect", this, index);
24524         this.fireEvent("selectionchange", this);
24525     },
24526
24527     // private
24528     restoreLast : function(){
24529         if(this._last){
24530             this.last = this._last;
24531         }
24532     },
24533
24534     // private
24535     acceptsNav : function(row, col, cm){
24536         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24537     },
24538
24539     // private
24540     onEditorKey : function(field, e){
24541         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24542         if(k == e.TAB){
24543             e.stopEvent();
24544             ed.completeEdit();
24545             if(e.shiftKey){
24546                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24547             }else{
24548                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24549             }
24550         }else if(k == e.ENTER && !e.ctrlKey){
24551             e.stopEvent();
24552             ed.completeEdit();
24553             if(e.shiftKey){
24554                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24555             }else{
24556                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24557             }
24558         }else if(k == e.ESC){
24559             ed.cancelEdit();
24560         }
24561         if(newCell){
24562             g.startEditing(newCell[0], newCell[1]);
24563         }
24564     }
24565 });
24566 /*
24567  * Based on:
24568  * Ext JS Library 1.1.1
24569  * Copyright(c) 2006-2007, Ext JS, LLC.
24570  *
24571  * Originally Released Under LGPL - original licence link has changed is not relivant.
24572  *
24573  * Fork - LGPL
24574  * <script type="text/javascript">
24575  */
24576  
24577 /**
24578  * @class Roo.bootstrap.PagingToolbar
24579  * @extends Roo.bootstrap.NavSimplebar
24580  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24581  * @constructor
24582  * Create a new PagingToolbar
24583  * @param {Object} config The config object
24584  * @param {Roo.data.Store} store
24585  */
24586 Roo.bootstrap.PagingToolbar = function(config)
24587 {
24588     // old args format still supported... - xtype is prefered..
24589         // created from xtype...
24590     
24591     this.ds = config.dataSource;
24592     
24593     if (config.store && !this.ds) {
24594         this.store= Roo.factory(config.store, Roo.data);
24595         this.ds = this.store;
24596         this.ds.xmodule = this.xmodule || false;
24597     }
24598     
24599     this.toolbarItems = [];
24600     if (config.items) {
24601         this.toolbarItems = config.items;
24602     }
24603     
24604     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24605     
24606     this.cursor = 0;
24607     
24608     if (this.ds) { 
24609         this.bind(this.ds);
24610     }
24611     
24612     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24613     
24614 };
24615
24616 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24617     /**
24618      * @cfg {Roo.data.Store} dataSource
24619      * The underlying data store providing the paged data
24620      */
24621     /**
24622      * @cfg {String/HTMLElement/Element} container
24623      * container The id or element that will contain the toolbar
24624      */
24625     /**
24626      * @cfg {Boolean} displayInfo
24627      * True to display the displayMsg (defaults to false)
24628      */
24629     /**
24630      * @cfg {Number} pageSize
24631      * The number of records to display per page (defaults to 20)
24632      */
24633     pageSize: 20,
24634     /**
24635      * @cfg {String} displayMsg
24636      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24637      */
24638     displayMsg : 'Displaying {0} - {1} of {2}',
24639     /**
24640      * @cfg {String} emptyMsg
24641      * The message to display when no records are found (defaults to "No data to display")
24642      */
24643     emptyMsg : 'No data to display',
24644     /**
24645      * Customizable piece of the default paging text (defaults to "Page")
24646      * @type String
24647      */
24648     beforePageText : "Page",
24649     /**
24650      * Customizable piece of the default paging text (defaults to "of %0")
24651      * @type String
24652      */
24653     afterPageText : "of {0}",
24654     /**
24655      * Customizable piece of the default paging text (defaults to "First Page")
24656      * @type String
24657      */
24658     firstText : "First Page",
24659     /**
24660      * Customizable piece of the default paging text (defaults to "Previous Page")
24661      * @type String
24662      */
24663     prevText : "Previous Page",
24664     /**
24665      * Customizable piece of the default paging text (defaults to "Next Page")
24666      * @type String
24667      */
24668     nextText : "Next Page",
24669     /**
24670      * Customizable piece of the default paging text (defaults to "Last Page")
24671      * @type String
24672      */
24673     lastText : "Last Page",
24674     /**
24675      * Customizable piece of the default paging text (defaults to "Refresh")
24676      * @type String
24677      */
24678     refreshText : "Refresh",
24679
24680     buttons : false,
24681     // private
24682     onRender : function(ct, position) 
24683     {
24684         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24685         this.navgroup.parentId = this.id;
24686         this.navgroup.onRender(this.el, null);
24687         // add the buttons to the navgroup
24688         
24689         if(this.displayInfo){
24690             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24691             this.displayEl = this.el.select('.x-paging-info', true).first();
24692 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24693 //            this.displayEl = navel.el.select('span',true).first();
24694         }
24695         
24696         var _this = this;
24697         
24698         if(this.buttons){
24699             Roo.each(_this.buttons, function(e){ // this might need to use render????
24700                Roo.factory(e).render(_this.el);
24701             });
24702         }
24703             
24704         Roo.each(_this.toolbarItems, function(e) {
24705             _this.navgroup.addItem(e);
24706         });
24707         
24708         
24709         this.first = this.navgroup.addItem({
24710             tooltip: this.firstText,
24711             cls: "prev",
24712             icon : 'fa fa-backward',
24713             disabled: true,
24714             preventDefault: true,
24715             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24716         });
24717         
24718         this.prev =  this.navgroup.addItem({
24719             tooltip: this.prevText,
24720             cls: "prev",
24721             icon : 'fa fa-step-backward',
24722             disabled: true,
24723             preventDefault: true,
24724             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24725         });
24726     //this.addSeparator();
24727         
24728         
24729         var field = this.navgroup.addItem( {
24730             tagtype : 'span',
24731             cls : 'x-paging-position',
24732             
24733             html : this.beforePageText  +
24734                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24735                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24736          } ); //?? escaped?
24737         
24738         this.field = field.el.select('input', true).first();
24739         this.field.on("keydown", this.onPagingKeydown, this);
24740         this.field.on("focus", function(){this.dom.select();});
24741     
24742     
24743         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24744         //this.field.setHeight(18);
24745         //this.addSeparator();
24746         this.next = this.navgroup.addItem({
24747             tooltip: this.nextText,
24748             cls: "next",
24749             html : ' <i class="fa fa-step-forward">',
24750             disabled: true,
24751             preventDefault: true,
24752             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24753         });
24754         this.last = this.navgroup.addItem({
24755             tooltip: this.lastText,
24756             icon : 'fa fa-forward',
24757             cls: "next",
24758             disabled: true,
24759             preventDefault: true,
24760             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24761         });
24762     //this.addSeparator();
24763         this.loading = this.navgroup.addItem({
24764             tooltip: this.refreshText,
24765             icon: 'fa fa-refresh',
24766             preventDefault: true,
24767             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24768         });
24769         
24770     },
24771
24772     // private
24773     updateInfo : function(){
24774         if(this.displayEl){
24775             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24776             var msg = count == 0 ?
24777                 this.emptyMsg :
24778                 String.format(
24779                     this.displayMsg,
24780                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24781                 );
24782             this.displayEl.update(msg);
24783         }
24784     },
24785
24786     // private
24787     onLoad : function(ds, r, o)
24788     {
24789         this.cursor = o.params.start ? o.params.start : 0;
24790         
24791         var d = this.getPageData(),
24792             ap = d.activePage,
24793             ps = d.pages;
24794         
24795         
24796         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24797         this.field.dom.value = ap;
24798         this.first.setDisabled(ap == 1);
24799         this.prev.setDisabled(ap == 1);
24800         this.next.setDisabled(ap == ps);
24801         this.last.setDisabled(ap == ps);
24802         this.loading.enable();
24803         this.updateInfo();
24804     },
24805
24806     // private
24807     getPageData : function(){
24808         var total = this.ds.getTotalCount();
24809         return {
24810             total : total,
24811             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24812             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24813         };
24814     },
24815
24816     // private
24817     onLoadError : function(){
24818         this.loading.enable();
24819     },
24820
24821     // private
24822     onPagingKeydown : function(e){
24823         var k = e.getKey();
24824         var d = this.getPageData();
24825         if(k == e.RETURN){
24826             var v = this.field.dom.value, pageNum;
24827             if(!v || isNaN(pageNum = parseInt(v, 10))){
24828                 this.field.dom.value = d.activePage;
24829                 return;
24830             }
24831             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24832             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24833             e.stopEvent();
24834         }
24835         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))
24836         {
24837           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24838           this.field.dom.value = pageNum;
24839           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24840           e.stopEvent();
24841         }
24842         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24843         {
24844           var v = this.field.dom.value, pageNum; 
24845           var increment = (e.shiftKey) ? 10 : 1;
24846           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24847                 increment *= -1;
24848           }
24849           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24850             this.field.dom.value = d.activePage;
24851             return;
24852           }
24853           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24854           {
24855             this.field.dom.value = parseInt(v, 10) + increment;
24856             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24857             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24858           }
24859           e.stopEvent();
24860         }
24861     },
24862
24863     // private
24864     beforeLoad : function(){
24865         if(this.loading){
24866             this.loading.disable();
24867         }
24868     },
24869
24870     // private
24871     onClick : function(which){
24872         
24873         var ds = this.ds;
24874         if (!ds) {
24875             return;
24876         }
24877         
24878         switch(which){
24879             case "first":
24880                 ds.load({params:{start: 0, limit: this.pageSize}});
24881             break;
24882             case "prev":
24883                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24884             break;
24885             case "next":
24886                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24887             break;
24888             case "last":
24889                 var total = ds.getTotalCount();
24890                 var extra = total % this.pageSize;
24891                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24892                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24893             break;
24894             case "refresh":
24895                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24896             break;
24897         }
24898     },
24899
24900     /**
24901      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24902      * @param {Roo.data.Store} store The data store to unbind
24903      */
24904     unbind : function(ds){
24905         ds.un("beforeload", this.beforeLoad, this);
24906         ds.un("load", this.onLoad, this);
24907         ds.un("loadexception", this.onLoadError, this);
24908         ds.un("remove", this.updateInfo, this);
24909         ds.un("add", this.updateInfo, this);
24910         this.ds = undefined;
24911     },
24912
24913     /**
24914      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24915      * @param {Roo.data.Store} store The data store to bind
24916      */
24917     bind : function(ds){
24918         ds.on("beforeload", this.beforeLoad, this);
24919         ds.on("load", this.onLoad, this);
24920         ds.on("loadexception", this.onLoadError, this);
24921         ds.on("remove", this.updateInfo, this);
24922         ds.on("add", this.updateInfo, this);
24923         this.ds = ds;
24924     }
24925 });/*
24926  * - LGPL
24927  *
24928  * element
24929  * 
24930  */
24931
24932 /**
24933  * @class Roo.bootstrap.MessageBar
24934  * @extends Roo.bootstrap.Component
24935  * Bootstrap MessageBar class
24936  * @cfg {String} html contents of the MessageBar
24937  * @cfg {String} weight (info | success | warning | danger) default info
24938  * @cfg {String} beforeClass insert the bar before the given class
24939  * @cfg {Boolean} closable (true | false) default false
24940  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24941  * 
24942  * @constructor
24943  * Create a new Element
24944  * @param {Object} config The config object
24945  */
24946
24947 Roo.bootstrap.MessageBar = function(config){
24948     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24949 };
24950
24951 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24952     
24953     html: '',
24954     weight: 'info',
24955     closable: false,
24956     fixed: false,
24957     beforeClass: 'bootstrap-sticky-wrap',
24958     
24959     getAutoCreate : function(){
24960         
24961         var cfg = {
24962             tag: 'div',
24963             cls: 'alert alert-dismissable alert-' + this.weight,
24964             cn: [
24965                 {
24966                     tag: 'span',
24967                     cls: 'message',
24968                     html: this.html || ''
24969                 }
24970             ]
24971         };
24972         
24973         if(this.fixed){
24974             cfg.cls += ' alert-messages-fixed';
24975         }
24976         
24977         if(this.closable){
24978             cfg.cn.push({
24979                 tag: 'button',
24980                 cls: 'close',
24981                 html: 'x'
24982             });
24983         }
24984         
24985         return cfg;
24986     },
24987     
24988     onRender : function(ct, position)
24989     {
24990         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24991         
24992         if(!this.el){
24993             var cfg = Roo.apply({},  this.getAutoCreate());
24994             cfg.id = Roo.id();
24995             
24996             if (this.cls) {
24997                 cfg.cls += ' ' + this.cls;
24998             }
24999             if (this.style) {
25000                 cfg.style = this.style;
25001             }
25002             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25003             
25004             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25005         }
25006         
25007         this.el.select('>button.close').on('click', this.hide, this);
25008         
25009     },
25010     
25011     show : function()
25012     {
25013         if (!this.rendered) {
25014             this.render();
25015         }
25016         
25017         this.el.show();
25018         
25019         this.fireEvent('show', this);
25020         
25021     },
25022     
25023     hide : function()
25024     {
25025         if (!this.rendered) {
25026             this.render();
25027         }
25028         
25029         this.el.hide();
25030         
25031         this.fireEvent('hide', this);
25032     },
25033     
25034     update : function()
25035     {
25036 //        var e = this.el.dom.firstChild;
25037 //        
25038 //        if(this.closable){
25039 //            e = e.nextSibling;
25040 //        }
25041 //        
25042 //        e.data = this.html || '';
25043
25044         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25045     }
25046    
25047 });
25048
25049  
25050
25051      /*
25052  * - LGPL
25053  *
25054  * Graph
25055  * 
25056  */
25057
25058
25059 /**
25060  * @class Roo.bootstrap.Graph
25061  * @extends Roo.bootstrap.Component
25062  * Bootstrap Graph class
25063 > Prameters
25064  -sm {number} sm 4
25065  -md {number} md 5
25066  @cfg {String} graphtype  bar | vbar | pie
25067  @cfg {number} g_x coodinator | centre x (pie)
25068  @cfg {number} g_y coodinator | centre y (pie)
25069  @cfg {number} g_r radius (pie)
25070  @cfg {number} g_height height of the chart (respected by all elements in the set)
25071  @cfg {number} g_width width of the chart (respected by all elements in the set)
25072  @cfg {Object} title The title of the chart
25073     
25074  -{Array}  values
25075  -opts (object) options for the chart 
25076      o {
25077      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25078      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25079      o vgutter (number)
25080      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.
25081      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25082      o to
25083      o stretch (boolean)
25084      o }
25085  -opts (object) options for the pie
25086      o{
25087      o cut
25088      o startAngle (number)
25089      o endAngle (number)
25090      } 
25091  *
25092  * @constructor
25093  * Create a new Input
25094  * @param {Object} config The config object
25095  */
25096
25097 Roo.bootstrap.Graph = function(config){
25098     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25099     
25100     this.addEvents({
25101         // img events
25102         /**
25103          * @event click
25104          * The img click event for the img.
25105          * @param {Roo.EventObject} e
25106          */
25107         "click" : true
25108     });
25109 };
25110
25111 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25112     
25113     sm: 4,
25114     md: 5,
25115     graphtype: 'bar',
25116     g_height: 250,
25117     g_width: 400,
25118     g_x: 50,
25119     g_y: 50,
25120     g_r: 30,
25121     opts:{
25122         //g_colors: this.colors,
25123         g_type: 'soft',
25124         g_gutter: '20%'
25125
25126     },
25127     title : false,
25128
25129     getAutoCreate : function(){
25130         
25131         var cfg = {
25132             tag: 'div',
25133             html : null
25134         };
25135         
25136         
25137         return  cfg;
25138     },
25139
25140     onRender : function(ct,position){
25141         
25142         
25143         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25144         
25145         if (typeof(Raphael) == 'undefined') {
25146             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25147             return;
25148         }
25149         
25150         this.raphael = Raphael(this.el.dom);
25151         
25152                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25153                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25154                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25155                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25156                 /*
25157                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25158                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25159                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25160                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25161                 
25162                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25163                 r.barchart(330, 10, 300, 220, data1);
25164                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25165                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25166                 */
25167                 
25168                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25169                 // r.barchart(30, 30, 560, 250,  xdata, {
25170                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25171                 //     axis : "0 0 1 1",
25172                 //     axisxlabels :  xdata
25173                 //     //yvalues : cols,
25174                    
25175                 // });
25176 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25177 //        
25178 //        this.load(null,xdata,{
25179 //                axis : "0 0 1 1",
25180 //                axisxlabels :  xdata
25181 //                });
25182
25183     },
25184
25185     load : function(graphtype,xdata,opts)
25186     {
25187         this.raphael.clear();
25188         if(!graphtype) {
25189             graphtype = this.graphtype;
25190         }
25191         if(!opts){
25192             opts = this.opts;
25193         }
25194         var r = this.raphael,
25195             fin = function () {
25196                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25197             },
25198             fout = function () {
25199                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25200             },
25201             pfin = function() {
25202                 this.sector.stop();
25203                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25204
25205                 if (this.label) {
25206                     this.label[0].stop();
25207                     this.label[0].attr({ r: 7.5 });
25208                     this.label[1].attr({ "font-weight": 800 });
25209                 }
25210             },
25211             pfout = function() {
25212                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25213
25214                 if (this.label) {
25215                     this.label[0].animate({ r: 5 }, 500, "bounce");
25216                     this.label[1].attr({ "font-weight": 400 });
25217                 }
25218             };
25219
25220         switch(graphtype){
25221             case 'bar':
25222                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25223                 break;
25224             case 'hbar':
25225                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25226                 break;
25227             case 'pie':
25228 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25229 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25230 //            
25231                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25232                 
25233                 break;
25234
25235         }
25236         
25237         if(this.title){
25238             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25239         }
25240         
25241     },
25242     
25243     setTitle: function(o)
25244     {
25245         this.title = o;
25246     },
25247     
25248     initEvents: function() {
25249         
25250         if(!this.href){
25251             this.el.on('click', this.onClick, this);
25252         }
25253     },
25254     
25255     onClick : function(e)
25256     {
25257         Roo.log('img onclick');
25258         this.fireEvent('click', this, e);
25259     }
25260    
25261 });
25262
25263  
25264 /*
25265  * - LGPL
25266  *
25267  * numberBox
25268  * 
25269  */
25270 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25271
25272 /**
25273  * @class Roo.bootstrap.dash.NumberBox
25274  * @extends Roo.bootstrap.Component
25275  * Bootstrap NumberBox class
25276  * @cfg {String} headline Box headline
25277  * @cfg {String} content Box content
25278  * @cfg {String} icon Box icon
25279  * @cfg {String} footer Footer text
25280  * @cfg {String} fhref Footer href
25281  * 
25282  * @constructor
25283  * Create a new NumberBox
25284  * @param {Object} config The config object
25285  */
25286
25287
25288 Roo.bootstrap.dash.NumberBox = function(config){
25289     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25290     
25291 };
25292
25293 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25294     
25295     headline : '',
25296     content : '',
25297     icon : '',
25298     footer : '',
25299     fhref : '',
25300     ficon : '',
25301     
25302     getAutoCreate : function(){
25303         
25304         var cfg = {
25305             tag : 'div',
25306             cls : 'small-box ',
25307             cn : [
25308                 {
25309                     tag : 'div',
25310                     cls : 'inner',
25311                     cn :[
25312                         {
25313                             tag : 'h3',
25314                             cls : 'roo-headline',
25315                             html : this.headline
25316                         },
25317                         {
25318                             tag : 'p',
25319                             cls : 'roo-content',
25320                             html : this.content
25321                         }
25322                     ]
25323                 }
25324             ]
25325         };
25326         
25327         if(this.icon){
25328             cfg.cn.push({
25329                 tag : 'div',
25330                 cls : 'icon',
25331                 cn :[
25332                     {
25333                         tag : 'i',
25334                         cls : 'ion ' + this.icon
25335                     }
25336                 ]
25337             });
25338         }
25339         
25340         if(this.footer){
25341             var footer = {
25342                 tag : 'a',
25343                 cls : 'small-box-footer',
25344                 href : this.fhref || '#',
25345                 html : this.footer
25346             };
25347             
25348             cfg.cn.push(footer);
25349             
25350         }
25351         
25352         return  cfg;
25353     },
25354
25355     onRender : function(ct,position){
25356         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25357
25358
25359        
25360                 
25361     },
25362
25363     setHeadline: function (value)
25364     {
25365         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25366     },
25367     
25368     setFooter: function (value, href)
25369     {
25370         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25371         
25372         if(href){
25373             this.el.select('a.small-box-footer',true).first().attr('href', href);
25374         }
25375         
25376     },
25377
25378     setContent: function (value)
25379     {
25380         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25381     },
25382
25383     initEvents: function() 
25384     {   
25385         
25386     }
25387     
25388 });
25389
25390  
25391 /*
25392  * - LGPL
25393  *
25394  * TabBox
25395  * 
25396  */
25397 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25398
25399 /**
25400  * @class Roo.bootstrap.dash.TabBox
25401  * @extends Roo.bootstrap.Component
25402  * Bootstrap TabBox class
25403  * @cfg {String} title Title of the TabBox
25404  * @cfg {String} icon Icon of the TabBox
25405  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25406  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25407  * 
25408  * @constructor
25409  * Create a new TabBox
25410  * @param {Object} config The config object
25411  */
25412
25413
25414 Roo.bootstrap.dash.TabBox = function(config){
25415     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25416     this.addEvents({
25417         // raw events
25418         /**
25419          * @event addpane
25420          * When a pane is added
25421          * @param {Roo.bootstrap.dash.TabPane} pane
25422          */
25423         "addpane" : true,
25424         /**
25425          * @event activatepane
25426          * When a pane is activated
25427          * @param {Roo.bootstrap.dash.TabPane} pane
25428          */
25429         "activatepane" : true
25430         
25431          
25432     });
25433     
25434     this.panes = [];
25435 };
25436
25437 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25438
25439     title : '',
25440     icon : false,
25441     showtabs : true,
25442     tabScrollable : false,
25443     
25444     getChildContainer : function()
25445     {
25446         return this.el.select('.tab-content', true).first();
25447     },
25448     
25449     getAutoCreate : function(){
25450         
25451         var header = {
25452             tag: 'li',
25453             cls: 'pull-left header',
25454             html: this.title,
25455             cn : []
25456         };
25457         
25458         if(this.icon){
25459             header.cn.push({
25460                 tag: 'i',
25461                 cls: 'fa ' + this.icon
25462             });
25463         }
25464         
25465         var h = {
25466             tag: 'ul',
25467             cls: 'nav nav-tabs pull-right',
25468             cn: [
25469                 header
25470             ]
25471         };
25472         
25473         if(this.tabScrollable){
25474             h = {
25475                 tag: 'div',
25476                 cls: 'tab-header',
25477                 cn: [
25478                     {
25479                         tag: 'ul',
25480                         cls: 'nav nav-tabs pull-right',
25481                         cn: [
25482                             header
25483                         ]
25484                     }
25485                 ]
25486             };
25487         }
25488         
25489         var cfg = {
25490             tag: 'div',
25491             cls: 'nav-tabs-custom',
25492             cn: [
25493                 h,
25494                 {
25495                     tag: 'div',
25496                     cls: 'tab-content no-padding',
25497                     cn: []
25498                 }
25499             ]
25500         };
25501
25502         return  cfg;
25503     },
25504     initEvents : function()
25505     {
25506         //Roo.log('add add pane handler');
25507         this.on('addpane', this.onAddPane, this);
25508     },
25509      /**
25510      * Updates the box title
25511      * @param {String} html to set the title to.
25512      */
25513     setTitle : function(value)
25514     {
25515         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25516     },
25517     onAddPane : function(pane)
25518     {
25519         this.panes.push(pane);
25520         //Roo.log('addpane');
25521         //Roo.log(pane);
25522         // tabs are rendere left to right..
25523         if(!this.showtabs){
25524             return;
25525         }
25526         
25527         var ctr = this.el.select('.nav-tabs', true).first();
25528          
25529          
25530         var existing = ctr.select('.nav-tab',true);
25531         var qty = existing.getCount();;
25532         
25533         
25534         var tab = ctr.createChild({
25535             tag : 'li',
25536             cls : 'nav-tab' + (qty ? '' : ' active'),
25537             cn : [
25538                 {
25539                     tag : 'a',
25540                     href:'#',
25541                     html : pane.title
25542                 }
25543             ]
25544         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25545         pane.tab = tab;
25546         
25547         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25548         if (!qty) {
25549             pane.el.addClass('active');
25550         }
25551         
25552                 
25553     },
25554     onTabClick : function(ev,un,ob,pane)
25555     {
25556         //Roo.log('tab - prev default');
25557         ev.preventDefault();
25558         
25559         
25560         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25561         pane.tab.addClass('active');
25562         //Roo.log(pane.title);
25563         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25564         // technically we should have a deactivate event.. but maybe add later.
25565         // and it should not de-activate the selected tab...
25566         this.fireEvent('activatepane', pane);
25567         pane.el.addClass('active');
25568         pane.fireEvent('activate');
25569         
25570         
25571     },
25572     
25573     getActivePane : function()
25574     {
25575         var r = false;
25576         Roo.each(this.panes, function(p) {
25577             if(p.el.hasClass('active')){
25578                 r = p;
25579                 return false;
25580             }
25581             
25582             return;
25583         });
25584         
25585         return r;
25586     }
25587     
25588     
25589 });
25590
25591  
25592 /*
25593  * - LGPL
25594  *
25595  * Tab pane
25596  * 
25597  */
25598 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25599 /**
25600  * @class Roo.bootstrap.TabPane
25601  * @extends Roo.bootstrap.Component
25602  * Bootstrap TabPane class
25603  * @cfg {Boolean} active (false | true) Default false
25604  * @cfg {String} title title of panel
25605
25606  * 
25607  * @constructor
25608  * Create a new TabPane
25609  * @param {Object} config The config object
25610  */
25611
25612 Roo.bootstrap.dash.TabPane = function(config){
25613     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25614     
25615     this.addEvents({
25616         // raw events
25617         /**
25618          * @event activate
25619          * When a pane is activated
25620          * @param {Roo.bootstrap.dash.TabPane} pane
25621          */
25622         "activate" : true
25623          
25624     });
25625 };
25626
25627 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25628     
25629     active : false,
25630     title : '',
25631     
25632     // the tabBox that this is attached to.
25633     tab : false,
25634      
25635     getAutoCreate : function() 
25636     {
25637         var cfg = {
25638             tag: 'div',
25639             cls: 'tab-pane'
25640         };
25641         
25642         if(this.active){
25643             cfg.cls += ' active';
25644         }
25645         
25646         return cfg;
25647     },
25648     initEvents  : function()
25649     {
25650         //Roo.log('trigger add pane handler');
25651         this.parent().fireEvent('addpane', this)
25652     },
25653     
25654      /**
25655      * Updates the tab title 
25656      * @param {String} html to set the title to.
25657      */
25658     setTitle: function(str)
25659     {
25660         if (!this.tab) {
25661             return;
25662         }
25663         this.title = str;
25664         this.tab.select('a', true).first().dom.innerHTML = str;
25665         
25666     }
25667     
25668     
25669     
25670 });
25671
25672  
25673
25674
25675  /*
25676  * - LGPL
25677  *
25678  * menu
25679  * 
25680  */
25681 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25682
25683 /**
25684  * @class Roo.bootstrap.menu.Menu
25685  * @extends Roo.bootstrap.Component
25686  * Bootstrap Menu class - container for Menu
25687  * @cfg {String} html Text of the menu
25688  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25689  * @cfg {String} icon Font awesome icon
25690  * @cfg {String} pos Menu align to (top | bottom) default bottom
25691  * 
25692  * 
25693  * @constructor
25694  * Create a new Menu
25695  * @param {Object} config The config object
25696  */
25697
25698
25699 Roo.bootstrap.menu.Menu = function(config){
25700     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25701     
25702     this.addEvents({
25703         /**
25704          * @event beforeshow
25705          * Fires before this menu is displayed
25706          * @param {Roo.bootstrap.menu.Menu} this
25707          */
25708         beforeshow : true,
25709         /**
25710          * @event beforehide
25711          * Fires before this menu is hidden
25712          * @param {Roo.bootstrap.menu.Menu} this
25713          */
25714         beforehide : true,
25715         /**
25716          * @event show
25717          * Fires after this menu is displayed
25718          * @param {Roo.bootstrap.menu.Menu} this
25719          */
25720         show : true,
25721         /**
25722          * @event hide
25723          * Fires after this menu is hidden
25724          * @param {Roo.bootstrap.menu.Menu} this
25725          */
25726         hide : true,
25727         /**
25728          * @event click
25729          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25730          * @param {Roo.bootstrap.menu.Menu} this
25731          * @param {Roo.EventObject} e
25732          */
25733         click : true
25734     });
25735     
25736 };
25737
25738 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25739     
25740     submenu : false,
25741     html : '',
25742     weight : 'default',
25743     icon : false,
25744     pos : 'bottom',
25745     
25746     
25747     getChildContainer : function() {
25748         if(this.isSubMenu){
25749             return this.el;
25750         }
25751         
25752         return this.el.select('ul.dropdown-menu', true).first();  
25753     },
25754     
25755     getAutoCreate : function()
25756     {
25757         var text = [
25758             {
25759                 tag : 'span',
25760                 cls : 'roo-menu-text',
25761                 html : this.html
25762             }
25763         ];
25764         
25765         if(this.icon){
25766             text.unshift({
25767                 tag : 'i',
25768                 cls : 'fa ' + this.icon
25769             })
25770         }
25771         
25772         
25773         var cfg = {
25774             tag : 'div',
25775             cls : 'btn-group',
25776             cn : [
25777                 {
25778                     tag : 'button',
25779                     cls : 'dropdown-button btn btn-' + this.weight,
25780                     cn : text
25781                 },
25782                 {
25783                     tag : 'button',
25784                     cls : 'dropdown-toggle btn btn-' + this.weight,
25785                     cn : [
25786                         {
25787                             tag : 'span',
25788                             cls : 'caret'
25789                         }
25790                     ]
25791                 },
25792                 {
25793                     tag : 'ul',
25794                     cls : 'dropdown-menu'
25795                 }
25796             ]
25797             
25798         };
25799         
25800         if(this.pos == 'top'){
25801             cfg.cls += ' dropup';
25802         }
25803         
25804         if(this.isSubMenu){
25805             cfg = {
25806                 tag : 'ul',
25807                 cls : 'dropdown-menu'
25808             }
25809         }
25810         
25811         return cfg;
25812     },
25813     
25814     onRender : function(ct, position)
25815     {
25816         this.isSubMenu = ct.hasClass('dropdown-submenu');
25817         
25818         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25819     },
25820     
25821     initEvents : function() 
25822     {
25823         if(this.isSubMenu){
25824             return;
25825         }
25826         
25827         this.hidden = true;
25828         
25829         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25830         this.triggerEl.on('click', this.onTriggerPress, this);
25831         
25832         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25833         this.buttonEl.on('click', this.onClick, this);
25834         
25835     },
25836     
25837     list : function()
25838     {
25839         if(this.isSubMenu){
25840             return this.el;
25841         }
25842         
25843         return this.el.select('ul.dropdown-menu', true).first();
25844     },
25845     
25846     onClick : function(e)
25847     {
25848         this.fireEvent("click", this, e);
25849     },
25850     
25851     onTriggerPress  : function(e)
25852     {   
25853         if (this.isVisible()) {
25854             this.hide();
25855         } else {
25856             this.show();
25857         }
25858     },
25859     
25860     isVisible : function(){
25861         return !this.hidden;
25862     },
25863     
25864     show : function()
25865     {
25866         this.fireEvent("beforeshow", this);
25867         
25868         this.hidden = false;
25869         this.el.addClass('open');
25870         
25871         Roo.get(document).on("mouseup", this.onMouseUp, this);
25872         
25873         this.fireEvent("show", this);
25874         
25875         
25876     },
25877     
25878     hide : function()
25879     {
25880         this.fireEvent("beforehide", this);
25881         
25882         this.hidden = true;
25883         this.el.removeClass('open');
25884         
25885         Roo.get(document).un("mouseup", this.onMouseUp);
25886         
25887         this.fireEvent("hide", this);
25888     },
25889     
25890     onMouseUp : function()
25891     {
25892         this.hide();
25893     }
25894     
25895 });
25896
25897  
25898  /*
25899  * - LGPL
25900  *
25901  * menu item
25902  * 
25903  */
25904 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25905
25906 /**
25907  * @class Roo.bootstrap.menu.Item
25908  * @extends Roo.bootstrap.Component
25909  * Bootstrap MenuItem class
25910  * @cfg {Boolean} submenu (true | false) default false
25911  * @cfg {String} html text of the item
25912  * @cfg {String} href the link
25913  * @cfg {Boolean} disable (true | false) default false
25914  * @cfg {Boolean} preventDefault (true | false) default true
25915  * @cfg {String} icon Font awesome icon
25916  * @cfg {String} pos Submenu align to (left | right) default right 
25917  * 
25918  * 
25919  * @constructor
25920  * Create a new Item
25921  * @param {Object} config The config object
25922  */
25923
25924
25925 Roo.bootstrap.menu.Item = function(config){
25926     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25927     this.addEvents({
25928         /**
25929          * @event mouseover
25930          * Fires when the mouse is hovering over this menu
25931          * @param {Roo.bootstrap.menu.Item} this
25932          * @param {Roo.EventObject} e
25933          */
25934         mouseover : true,
25935         /**
25936          * @event mouseout
25937          * Fires when the mouse exits this menu
25938          * @param {Roo.bootstrap.menu.Item} this
25939          * @param {Roo.EventObject} e
25940          */
25941         mouseout : true,
25942         // raw events
25943         /**
25944          * @event click
25945          * The raw click event for the entire grid.
25946          * @param {Roo.EventObject} e
25947          */
25948         click : true
25949     });
25950 };
25951
25952 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25953     
25954     submenu : false,
25955     href : '',
25956     html : '',
25957     preventDefault: true,
25958     disable : false,
25959     icon : false,
25960     pos : 'right',
25961     
25962     getAutoCreate : function()
25963     {
25964         var text = [
25965             {
25966                 tag : 'span',
25967                 cls : 'roo-menu-item-text',
25968                 html : this.html
25969             }
25970         ];
25971         
25972         if(this.icon){
25973             text.unshift({
25974                 tag : 'i',
25975                 cls : 'fa ' + this.icon
25976             })
25977         }
25978         
25979         var cfg = {
25980             tag : 'li',
25981             cn : [
25982                 {
25983                     tag : 'a',
25984                     href : this.href || '#',
25985                     cn : text
25986                 }
25987             ]
25988         };
25989         
25990         if(this.disable){
25991             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25992         }
25993         
25994         if(this.submenu){
25995             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25996             
25997             if(this.pos == 'left'){
25998                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25999             }
26000         }
26001         
26002         return cfg;
26003     },
26004     
26005     initEvents : function() 
26006     {
26007         this.el.on('mouseover', this.onMouseOver, this);
26008         this.el.on('mouseout', this.onMouseOut, this);
26009         
26010         this.el.select('a', true).first().on('click', this.onClick, this);
26011         
26012     },
26013     
26014     onClick : function(e)
26015     {
26016         if(this.preventDefault){
26017             e.preventDefault();
26018         }
26019         
26020         this.fireEvent("click", this, e);
26021     },
26022     
26023     onMouseOver : function(e)
26024     {
26025         if(this.submenu && this.pos == 'left'){
26026             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26027         }
26028         
26029         this.fireEvent("mouseover", this, e);
26030     },
26031     
26032     onMouseOut : function(e)
26033     {
26034         this.fireEvent("mouseout", this, e);
26035     }
26036 });
26037
26038  
26039
26040  /*
26041  * - LGPL
26042  *
26043  * menu separator
26044  * 
26045  */
26046 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26047
26048 /**
26049  * @class Roo.bootstrap.menu.Separator
26050  * @extends Roo.bootstrap.Component
26051  * Bootstrap Separator class
26052  * 
26053  * @constructor
26054  * Create a new Separator
26055  * @param {Object} config The config object
26056  */
26057
26058
26059 Roo.bootstrap.menu.Separator = function(config){
26060     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26061 };
26062
26063 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26064     
26065     getAutoCreate : function(){
26066         var cfg = {
26067             tag : 'li',
26068             cls: 'divider'
26069         };
26070         
26071         return cfg;
26072     }
26073    
26074 });
26075
26076  
26077
26078  /*
26079  * - LGPL
26080  *
26081  * Tooltip
26082  * 
26083  */
26084
26085 /**
26086  * @class Roo.bootstrap.Tooltip
26087  * Bootstrap Tooltip class
26088  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26089  * to determine which dom element triggers the tooltip.
26090  * 
26091  * It needs to add support for additional attributes like tooltip-position
26092  * 
26093  * @constructor
26094  * Create a new Toolti
26095  * @param {Object} config The config object
26096  */
26097
26098 Roo.bootstrap.Tooltip = function(config){
26099     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26100     
26101     this.alignment = Roo.bootstrap.Tooltip.alignment;
26102     
26103     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26104         this.alignment = config.alignment;
26105     }
26106     
26107 };
26108
26109 Roo.apply(Roo.bootstrap.Tooltip, {
26110     /**
26111      * @function init initialize tooltip monitoring.
26112      * @static
26113      */
26114     currentEl : false,
26115     currentTip : false,
26116     currentRegion : false,
26117     
26118     //  init : delay?
26119     
26120     init : function()
26121     {
26122         Roo.get(document).on('mouseover', this.enter ,this);
26123         Roo.get(document).on('mouseout', this.leave, this);
26124          
26125         
26126         this.currentTip = new Roo.bootstrap.Tooltip();
26127     },
26128     
26129     enter : function(ev)
26130     {
26131         var dom = ev.getTarget();
26132         
26133         //Roo.log(['enter',dom]);
26134         var el = Roo.fly(dom);
26135         if (this.currentEl) {
26136             //Roo.log(dom);
26137             //Roo.log(this.currentEl);
26138             //Roo.log(this.currentEl.contains(dom));
26139             if (this.currentEl == el) {
26140                 return;
26141             }
26142             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26143                 return;
26144             }
26145
26146         }
26147         
26148         if (this.currentTip.el) {
26149             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26150         }    
26151         //Roo.log(ev);
26152         
26153         if(!el || el.dom == document){
26154             return;
26155         }
26156         
26157         var bindEl = el;
26158         
26159         // you can not look for children, as if el is the body.. then everythign is the child..
26160         if (!el.attr('tooltip')) { //
26161             if (!el.select("[tooltip]").elements.length) {
26162                 return;
26163             }
26164             // is the mouse over this child...?
26165             bindEl = el.select("[tooltip]").first();
26166             var xy = ev.getXY();
26167             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26168                 //Roo.log("not in region.");
26169                 return;
26170             }
26171             //Roo.log("child element over..");
26172             
26173         }
26174         this.currentEl = bindEl;
26175         this.currentTip.bind(bindEl);
26176         this.currentRegion = Roo.lib.Region.getRegion(dom);
26177         this.currentTip.enter();
26178         
26179     },
26180     leave : function(ev)
26181     {
26182         var dom = ev.getTarget();
26183         //Roo.log(['leave',dom]);
26184         if (!this.currentEl) {
26185             return;
26186         }
26187         
26188         
26189         if (dom != this.currentEl.dom) {
26190             return;
26191         }
26192         var xy = ev.getXY();
26193         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26194             return;
26195         }
26196         // only activate leave if mouse cursor is outside... bounding box..
26197         
26198         
26199         
26200         
26201         if (this.currentTip) {
26202             this.currentTip.leave();
26203         }
26204         //Roo.log('clear currentEl');
26205         this.currentEl = false;
26206         
26207         
26208     },
26209     alignment : {
26210         'left' : ['r-l', [-2,0], 'right'],
26211         'right' : ['l-r', [2,0], 'left'],
26212         'bottom' : ['t-b', [0,2], 'top'],
26213         'top' : [ 'b-t', [0,-2], 'bottom']
26214     }
26215     
26216 });
26217
26218
26219 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26220     
26221     
26222     bindEl : false,
26223     
26224     delay : null, // can be { show : 300 , hide: 500}
26225     
26226     timeout : null,
26227     
26228     hoverState : null, //???
26229     
26230     placement : 'bottom', 
26231     
26232     alignment : false,
26233     
26234     getAutoCreate : function(){
26235     
26236         var cfg = {
26237            cls : 'tooltip',
26238            role : 'tooltip',
26239            cn : [
26240                 {
26241                     cls : 'tooltip-arrow'
26242                 },
26243                 {
26244                     cls : 'tooltip-inner'
26245                 }
26246            ]
26247         };
26248         
26249         return cfg;
26250     },
26251     bind : function(el)
26252     {
26253         this.bindEl = el;
26254     },
26255       
26256     
26257     enter : function () {
26258        
26259         if (this.timeout != null) {
26260             clearTimeout(this.timeout);
26261         }
26262         
26263         this.hoverState = 'in';
26264          //Roo.log("enter - show");
26265         if (!this.delay || !this.delay.show) {
26266             this.show();
26267             return;
26268         }
26269         var _t = this;
26270         this.timeout = setTimeout(function () {
26271             if (_t.hoverState == 'in') {
26272                 _t.show();
26273             }
26274         }, this.delay.show);
26275     },
26276     leave : function()
26277     {
26278         clearTimeout(this.timeout);
26279     
26280         this.hoverState = 'out';
26281          if (!this.delay || !this.delay.hide) {
26282             this.hide();
26283             return;
26284         }
26285        
26286         var _t = this;
26287         this.timeout = setTimeout(function () {
26288             //Roo.log("leave - timeout");
26289             
26290             if (_t.hoverState == 'out') {
26291                 _t.hide();
26292                 Roo.bootstrap.Tooltip.currentEl = false;
26293             }
26294         }, delay);
26295     },
26296     
26297     show : function (msg)
26298     {
26299         if (!this.el) {
26300             this.render(document.body);
26301         }
26302         // set content.
26303         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26304         
26305         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26306         
26307         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26308         
26309         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26310         
26311         var placement = typeof this.placement == 'function' ?
26312             this.placement.call(this, this.el, on_el) :
26313             this.placement;
26314             
26315         var autoToken = /\s?auto?\s?/i;
26316         var autoPlace = autoToken.test(placement);
26317         if (autoPlace) {
26318             placement = placement.replace(autoToken, '') || 'top';
26319         }
26320         
26321         //this.el.detach()
26322         //this.el.setXY([0,0]);
26323         this.el.show();
26324         //this.el.dom.style.display='block';
26325         
26326         //this.el.appendTo(on_el);
26327         
26328         var p = this.getPosition();
26329         var box = this.el.getBox();
26330         
26331         if (autoPlace) {
26332             // fixme..
26333         }
26334         
26335         var align = this.alignment[placement];
26336         
26337         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26338         
26339         if(placement == 'top' || placement == 'bottom'){
26340             if(xy[0] < 0){
26341                 placement = 'right';
26342             }
26343             
26344             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26345                 placement = 'left';
26346             }
26347             
26348             var scroll = Roo.select('body', true).first().getScroll();
26349             
26350             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26351                 placement = 'top';
26352             }
26353             
26354             align = this.alignment[placement];
26355         }
26356         
26357         this.el.alignTo(this.bindEl, align[0],align[1]);
26358         //var arrow = this.el.select('.arrow',true).first();
26359         //arrow.set(align[2], 
26360         
26361         this.el.addClass(placement);
26362         
26363         this.el.addClass('in fade');
26364         
26365         this.hoverState = null;
26366         
26367         if (this.el.hasClass('fade')) {
26368             // fade it?
26369         }
26370         
26371     },
26372     hide : function()
26373     {
26374          
26375         if (!this.el) {
26376             return;
26377         }
26378         //this.el.setXY([0,0]);
26379         this.el.removeClass('in');
26380         //this.el.hide();
26381         
26382     }
26383     
26384 });
26385  
26386
26387  /*
26388  * - LGPL
26389  *
26390  * Location Picker
26391  * 
26392  */
26393
26394 /**
26395  * @class Roo.bootstrap.LocationPicker
26396  * @extends Roo.bootstrap.Component
26397  * Bootstrap LocationPicker class
26398  * @cfg {Number} latitude Position when init default 0
26399  * @cfg {Number} longitude Position when init default 0
26400  * @cfg {Number} zoom default 15
26401  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26402  * @cfg {Boolean} mapTypeControl default false
26403  * @cfg {Boolean} disableDoubleClickZoom default false
26404  * @cfg {Boolean} scrollwheel default true
26405  * @cfg {Boolean} streetViewControl default false
26406  * @cfg {Number} radius default 0
26407  * @cfg {String} locationName
26408  * @cfg {Boolean} draggable default true
26409  * @cfg {Boolean} enableAutocomplete default false
26410  * @cfg {Boolean} enableReverseGeocode default true
26411  * @cfg {String} markerTitle
26412  * 
26413  * @constructor
26414  * Create a new LocationPicker
26415  * @param {Object} config The config object
26416  */
26417
26418
26419 Roo.bootstrap.LocationPicker = function(config){
26420     
26421     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26422     
26423     this.addEvents({
26424         /**
26425          * @event initial
26426          * Fires when the picker initialized.
26427          * @param {Roo.bootstrap.LocationPicker} this
26428          * @param {Google Location} location
26429          */
26430         initial : true,
26431         /**
26432          * @event positionchanged
26433          * Fires when the picker position changed.
26434          * @param {Roo.bootstrap.LocationPicker} this
26435          * @param {Google Location} location
26436          */
26437         positionchanged : true,
26438         /**
26439          * @event resize
26440          * Fires when the map resize.
26441          * @param {Roo.bootstrap.LocationPicker} this
26442          */
26443         resize : true,
26444         /**
26445          * @event show
26446          * Fires when the map show.
26447          * @param {Roo.bootstrap.LocationPicker} this
26448          */
26449         show : true,
26450         /**
26451          * @event hide
26452          * Fires when the map hide.
26453          * @param {Roo.bootstrap.LocationPicker} this
26454          */
26455         hide : true,
26456         /**
26457          * @event mapClick
26458          * Fires when click the map.
26459          * @param {Roo.bootstrap.LocationPicker} this
26460          * @param {Map event} e
26461          */
26462         mapClick : true,
26463         /**
26464          * @event mapRightClick
26465          * Fires when right click the map.
26466          * @param {Roo.bootstrap.LocationPicker} this
26467          * @param {Map event} e
26468          */
26469         mapRightClick : true,
26470         /**
26471          * @event markerClick
26472          * Fires when click the marker.
26473          * @param {Roo.bootstrap.LocationPicker} this
26474          * @param {Map event} e
26475          */
26476         markerClick : true,
26477         /**
26478          * @event markerRightClick
26479          * Fires when right click the marker.
26480          * @param {Roo.bootstrap.LocationPicker} this
26481          * @param {Map event} e
26482          */
26483         markerRightClick : true,
26484         /**
26485          * @event OverlayViewDraw
26486          * Fires when OverlayView Draw
26487          * @param {Roo.bootstrap.LocationPicker} this
26488          */
26489         OverlayViewDraw : true,
26490         /**
26491          * @event OverlayViewOnAdd
26492          * Fires when OverlayView Draw
26493          * @param {Roo.bootstrap.LocationPicker} this
26494          */
26495         OverlayViewOnAdd : true,
26496         /**
26497          * @event OverlayViewOnRemove
26498          * Fires when OverlayView Draw
26499          * @param {Roo.bootstrap.LocationPicker} this
26500          */
26501         OverlayViewOnRemove : true,
26502         /**
26503          * @event OverlayViewShow
26504          * Fires when OverlayView Draw
26505          * @param {Roo.bootstrap.LocationPicker} this
26506          * @param {Pixel} cpx
26507          */
26508         OverlayViewShow : true,
26509         /**
26510          * @event OverlayViewHide
26511          * Fires when OverlayView Draw
26512          * @param {Roo.bootstrap.LocationPicker} this
26513          */
26514         OverlayViewHide : true,
26515         /**
26516          * @event loadexception
26517          * Fires when load google lib failed.
26518          * @param {Roo.bootstrap.LocationPicker} this
26519          */
26520         loadexception : true
26521     });
26522         
26523 };
26524
26525 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26526     
26527     gMapContext: false,
26528     
26529     latitude: 0,
26530     longitude: 0,
26531     zoom: 15,
26532     mapTypeId: false,
26533     mapTypeControl: false,
26534     disableDoubleClickZoom: false,
26535     scrollwheel: true,
26536     streetViewControl: false,
26537     radius: 0,
26538     locationName: '',
26539     draggable: true,
26540     enableAutocomplete: false,
26541     enableReverseGeocode: true,
26542     markerTitle: '',
26543     
26544     getAutoCreate: function()
26545     {
26546
26547         var cfg = {
26548             tag: 'div',
26549             cls: 'roo-location-picker'
26550         };
26551         
26552         return cfg
26553     },
26554     
26555     initEvents: function(ct, position)
26556     {       
26557         if(!this.el.getWidth() || this.isApplied()){
26558             return;
26559         }
26560         
26561         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26562         
26563         this.initial();
26564     },
26565     
26566     initial: function()
26567     {
26568         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26569             this.fireEvent('loadexception', this);
26570             return;
26571         }
26572         
26573         if(!this.mapTypeId){
26574             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26575         }
26576         
26577         this.gMapContext = this.GMapContext();
26578         
26579         this.initOverlayView();
26580         
26581         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26582         
26583         var _this = this;
26584                 
26585         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26586             _this.setPosition(_this.gMapContext.marker.position);
26587         });
26588         
26589         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26590             _this.fireEvent('mapClick', this, event);
26591             
26592         });
26593
26594         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26595             _this.fireEvent('mapRightClick', this, event);
26596             
26597         });
26598         
26599         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26600             _this.fireEvent('markerClick', this, event);
26601             
26602         });
26603
26604         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26605             _this.fireEvent('markerRightClick', this, event);
26606             
26607         });
26608         
26609         this.setPosition(this.gMapContext.location);
26610         
26611         this.fireEvent('initial', this, this.gMapContext.location);
26612     },
26613     
26614     initOverlayView: function()
26615     {
26616         var _this = this;
26617         
26618         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26619             
26620             draw: function()
26621             {
26622                 _this.fireEvent('OverlayViewDraw', _this);
26623             },
26624             
26625             onAdd: function()
26626             {
26627                 _this.fireEvent('OverlayViewOnAdd', _this);
26628             },
26629             
26630             onRemove: function()
26631             {
26632                 _this.fireEvent('OverlayViewOnRemove', _this);
26633             },
26634             
26635             show: function(cpx)
26636             {
26637                 _this.fireEvent('OverlayViewShow', _this, cpx);
26638             },
26639             
26640             hide: function()
26641             {
26642                 _this.fireEvent('OverlayViewHide', _this);
26643             }
26644             
26645         });
26646     },
26647     
26648     fromLatLngToContainerPixel: function(event)
26649     {
26650         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26651     },
26652     
26653     isApplied: function() 
26654     {
26655         return this.getGmapContext() == false ? false : true;
26656     },
26657     
26658     getGmapContext: function() 
26659     {
26660         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26661     },
26662     
26663     GMapContext: function() 
26664     {
26665         var position = new google.maps.LatLng(this.latitude, this.longitude);
26666         
26667         var _map = new google.maps.Map(this.el.dom, {
26668             center: position,
26669             zoom: this.zoom,
26670             mapTypeId: this.mapTypeId,
26671             mapTypeControl: this.mapTypeControl,
26672             disableDoubleClickZoom: this.disableDoubleClickZoom,
26673             scrollwheel: this.scrollwheel,
26674             streetViewControl: this.streetViewControl,
26675             locationName: this.locationName,
26676             draggable: this.draggable,
26677             enableAutocomplete: this.enableAutocomplete,
26678             enableReverseGeocode: this.enableReverseGeocode
26679         });
26680         
26681         var _marker = new google.maps.Marker({
26682             position: position,
26683             map: _map,
26684             title: this.markerTitle,
26685             draggable: this.draggable
26686         });
26687         
26688         return {
26689             map: _map,
26690             marker: _marker,
26691             circle: null,
26692             location: position,
26693             radius: this.radius,
26694             locationName: this.locationName,
26695             addressComponents: {
26696                 formatted_address: null,
26697                 addressLine1: null,
26698                 addressLine2: null,
26699                 streetName: null,
26700                 streetNumber: null,
26701                 city: null,
26702                 district: null,
26703                 state: null,
26704                 stateOrProvince: null
26705             },
26706             settings: this,
26707             domContainer: this.el.dom,
26708             geodecoder: new google.maps.Geocoder()
26709         };
26710     },
26711     
26712     drawCircle: function(center, radius, options) 
26713     {
26714         if (this.gMapContext.circle != null) {
26715             this.gMapContext.circle.setMap(null);
26716         }
26717         if (radius > 0) {
26718             radius *= 1;
26719             options = Roo.apply({}, options, {
26720                 strokeColor: "#0000FF",
26721                 strokeOpacity: .35,
26722                 strokeWeight: 2,
26723                 fillColor: "#0000FF",
26724                 fillOpacity: .2
26725             });
26726             
26727             options.map = this.gMapContext.map;
26728             options.radius = radius;
26729             options.center = center;
26730             this.gMapContext.circle = new google.maps.Circle(options);
26731             return this.gMapContext.circle;
26732         }
26733         
26734         return null;
26735     },
26736     
26737     setPosition: function(location) 
26738     {
26739         this.gMapContext.location = location;
26740         this.gMapContext.marker.setPosition(location);
26741         this.gMapContext.map.panTo(location);
26742         this.drawCircle(location, this.gMapContext.radius, {});
26743         
26744         var _this = this;
26745         
26746         if (this.gMapContext.settings.enableReverseGeocode) {
26747             this.gMapContext.geodecoder.geocode({
26748                 latLng: this.gMapContext.location
26749             }, function(results, status) {
26750                 
26751                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26752                     _this.gMapContext.locationName = results[0].formatted_address;
26753                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26754                     
26755                     _this.fireEvent('positionchanged', this, location);
26756                 }
26757             });
26758             
26759             return;
26760         }
26761         
26762         this.fireEvent('positionchanged', this, location);
26763     },
26764     
26765     resize: function()
26766     {
26767         google.maps.event.trigger(this.gMapContext.map, "resize");
26768         
26769         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26770         
26771         this.fireEvent('resize', this);
26772     },
26773     
26774     setPositionByLatLng: function(latitude, longitude)
26775     {
26776         this.setPosition(new google.maps.LatLng(latitude, longitude));
26777     },
26778     
26779     getCurrentPosition: function() 
26780     {
26781         return {
26782             latitude: this.gMapContext.location.lat(),
26783             longitude: this.gMapContext.location.lng()
26784         };
26785     },
26786     
26787     getAddressName: function() 
26788     {
26789         return this.gMapContext.locationName;
26790     },
26791     
26792     getAddressComponents: function() 
26793     {
26794         return this.gMapContext.addressComponents;
26795     },
26796     
26797     address_component_from_google_geocode: function(address_components) 
26798     {
26799         var result = {};
26800         
26801         for (var i = 0; i < address_components.length; i++) {
26802             var component = address_components[i];
26803             if (component.types.indexOf("postal_code") >= 0) {
26804                 result.postalCode = component.short_name;
26805             } else if (component.types.indexOf("street_number") >= 0) {
26806                 result.streetNumber = component.short_name;
26807             } else if (component.types.indexOf("route") >= 0) {
26808                 result.streetName = component.short_name;
26809             } else if (component.types.indexOf("neighborhood") >= 0) {
26810                 result.city = component.short_name;
26811             } else if (component.types.indexOf("locality") >= 0) {
26812                 result.city = component.short_name;
26813             } else if (component.types.indexOf("sublocality") >= 0) {
26814                 result.district = component.short_name;
26815             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26816                 result.stateOrProvince = component.short_name;
26817             } else if (component.types.indexOf("country") >= 0) {
26818                 result.country = component.short_name;
26819             }
26820         }
26821         
26822         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26823         result.addressLine2 = "";
26824         return result;
26825     },
26826     
26827     setZoomLevel: function(zoom)
26828     {
26829         this.gMapContext.map.setZoom(zoom);
26830     },
26831     
26832     show: function()
26833     {
26834         if(!this.el){
26835             return;
26836         }
26837         
26838         this.el.show();
26839         
26840         this.resize();
26841         
26842         this.fireEvent('show', this);
26843     },
26844     
26845     hide: function()
26846     {
26847         if(!this.el){
26848             return;
26849         }
26850         
26851         this.el.hide();
26852         
26853         this.fireEvent('hide', this);
26854     }
26855     
26856 });
26857
26858 Roo.apply(Roo.bootstrap.LocationPicker, {
26859     
26860     OverlayView : function(map, options)
26861     {
26862         options = options || {};
26863         
26864         this.setMap(map);
26865     }
26866     
26867     
26868 });/*
26869  * - LGPL
26870  *
26871  * Alert
26872  * 
26873  */
26874
26875 /**
26876  * @class Roo.bootstrap.Alert
26877  * @extends Roo.bootstrap.Component
26878  * Bootstrap Alert class
26879  * @cfg {String} title The title of alert
26880  * @cfg {String} html The content of alert
26881  * @cfg {String} weight (  success | info | warning | danger )
26882  * @cfg {String} faicon font-awesomeicon
26883  * 
26884  * @constructor
26885  * Create a new alert
26886  * @param {Object} config The config object
26887  */
26888
26889
26890 Roo.bootstrap.Alert = function(config){
26891     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26892     
26893 };
26894
26895 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26896     
26897     title: '',
26898     html: '',
26899     weight: false,
26900     faicon: false,
26901     
26902     getAutoCreate : function()
26903     {
26904         
26905         var cfg = {
26906             tag : 'div',
26907             cls : 'alert',
26908             cn : [
26909                 {
26910                     tag : 'i',
26911                     cls : 'roo-alert-icon'
26912                     
26913                 },
26914                 {
26915                     tag : 'b',
26916                     cls : 'roo-alert-title',
26917                     html : this.title
26918                 },
26919                 {
26920                     tag : 'span',
26921                     cls : 'roo-alert-text',
26922                     html : this.html
26923                 }
26924             ]
26925         };
26926         
26927         if(this.faicon){
26928             cfg.cn[0].cls += ' fa ' + this.faicon;
26929         }
26930         
26931         if(this.weight){
26932             cfg.cls += ' alert-' + this.weight;
26933         }
26934         
26935         return cfg;
26936     },
26937     
26938     initEvents: function() 
26939     {
26940         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26941     },
26942     
26943     setTitle : function(str)
26944     {
26945         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26946     },
26947     
26948     setText : function(str)
26949     {
26950         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26951     },
26952     
26953     setWeight : function(weight)
26954     {
26955         if(this.weight){
26956             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26957         }
26958         
26959         this.weight = weight;
26960         
26961         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26962     },
26963     
26964     setIcon : function(icon)
26965     {
26966         if(this.faicon){
26967             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26968         }
26969         
26970         this.faicon = icon;
26971         
26972         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26973     },
26974     
26975     hide: function() 
26976     {
26977         this.el.hide();   
26978     },
26979     
26980     show: function() 
26981     {  
26982         this.el.show();   
26983     }
26984     
26985 });
26986
26987  
26988 /*
26989 * Licence: LGPL
26990 */
26991
26992 /**
26993  * @class Roo.bootstrap.UploadCropbox
26994  * @extends Roo.bootstrap.Component
26995  * Bootstrap UploadCropbox class
26996  * @cfg {String} emptyText show when image has been loaded
26997  * @cfg {String} rotateNotify show when image too small to rotate
26998  * @cfg {Number} errorTimeout default 3000
26999  * @cfg {Number} minWidth default 300
27000  * @cfg {Number} minHeight default 300
27001  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27002  * @cfg {Boolean} isDocument (true|false) default false
27003  * @cfg {String} url action url
27004  * @cfg {String} paramName default 'imageUpload'
27005  * @cfg {String} method default POST
27006  * @cfg {Boolean} loadMask (true|false) default true
27007  * @cfg {Boolean} loadingText default 'Loading...'
27008  * 
27009  * @constructor
27010  * Create a new UploadCropbox
27011  * @param {Object} config The config object
27012  */
27013
27014 Roo.bootstrap.UploadCropbox = function(config){
27015     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27016     
27017     this.addEvents({
27018         /**
27019          * @event beforeselectfile
27020          * Fire before select file
27021          * @param {Roo.bootstrap.UploadCropbox} this
27022          */
27023         "beforeselectfile" : true,
27024         /**
27025          * @event initial
27026          * Fire after initEvent
27027          * @param {Roo.bootstrap.UploadCropbox} this
27028          */
27029         "initial" : true,
27030         /**
27031          * @event crop
27032          * Fire after initEvent
27033          * @param {Roo.bootstrap.UploadCropbox} this
27034          * @param {String} data
27035          */
27036         "crop" : true,
27037         /**
27038          * @event prepare
27039          * Fire when preparing the file data
27040          * @param {Roo.bootstrap.UploadCropbox} this
27041          * @param {Object} file
27042          */
27043         "prepare" : true,
27044         /**
27045          * @event exception
27046          * Fire when get exception
27047          * @param {Roo.bootstrap.UploadCropbox} this
27048          * @param {XMLHttpRequest} xhr
27049          */
27050         "exception" : true,
27051         /**
27052          * @event beforeloadcanvas
27053          * Fire before load the canvas
27054          * @param {Roo.bootstrap.UploadCropbox} this
27055          * @param {String} src
27056          */
27057         "beforeloadcanvas" : true,
27058         /**
27059          * @event trash
27060          * Fire when trash image
27061          * @param {Roo.bootstrap.UploadCropbox} this
27062          */
27063         "trash" : true,
27064         /**
27065          * @event download
27066          * Fire when download the image
27067          * @param {Roo.bootstrap.UploadCropbox} this
27068          */
27069         "download" : true,
27070         /**
27071          * @event footerbuttonclick
27072          * Fire when footerbuttonclick
27073          * @param {Roo.bootstrap.UploadCropbox} this
27074          * @param {String} type
27075          */
27076         "footerbuttonclick" : true,
27077         /**
27078          * @event resize
27079          * Fire when resize
27080          * @param {Roo.bootstrap.UploadCropbox} this
27081          */
27082         "resize" : true,
27083         /**
27084          * @event rotate
27085          * Fire when rotate the image
27086          * @param {Roo.bootstrap.UploadCropbox} this
27087          * @param {String} pos
27088          */
27089         "rotate" : true,
27090         /**
27091          * @event inspect
27092          * Fire when inspect the file
27093          * @param {Roo.bootstrap.UploadCropbox} this
27094          * @param {Object} file
27095          */
27096         "inspect" : true,
27097         /**
27098          * @event upload
27099          * Fire when xhr upload the file
27100          * @param {Roo.bootstrap.UploadCropbox} this
27101          * @param {Object} data
27102          */
27103         "upload" : true,
27104         /**
27105          * @event arrange
27106          * Fire when arrange the file data
27107          * @param {Roo.bootstrap.UploadCropbox} this
27108          * @param {Object} formData
27109          */
27110         "arrange" : true
27111     });
27112     
27113     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27114 };
27115
27116 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27117     
27118     emptyText : 'Click to upload image',
27119     rotateNotify : 'Image is too small to rotate',
27120     errorTimeout : 3000,
27121     scale : 0,
27122     baseScale : 1,
27123     rotate : 0,
27124     dragable : false,
27125     pinching : false,
27126     mouseX : 0,
27127     mouseY : 0,
27128     cropData : false,
27129     minWidth : 300,
27130     minHeight : 300,
27131     file : false,
27132     exif : {},
27133     baseRotate : 1,
27134     cropType : 'image/jpeg',
27135     buttons : false,
27136     canvasLoaded : false,
27137     isDocument : false,
27138     method : 'POST',
27139     paramName : 'imageUpload',
27140     loadMask : true,
27141     loadingText : 'Loading...',
27142     maskEl : false,
27143     
27144     getAutoCreate : function()
27145     {
27146         var cfg = {
27147             tag : 'div',
27148             cls : 'roo-upload-cropbox',
27149             cn : [
27150                 {
27151                     tag : 'input',
27152                     cls : 'roo-upload-cropbox-selector',
27153                     type : 'file'
27154                 },
27155                 {
27156                     tag : 'div',
27157                     cls : 'roo-upload-cropbox-body',
27158                     style : 'cursor:pointer',
27159                     cn : [
27160                         {
27161                             tag : 'div',
27162                             cls : 'roo-upload-cropbox-preview'
27163                         },
27164                         {
27165                             tag : 'div',
27166                             cls : 'roo-upload-cropbox-thumb'
27167                         },
27168                         {
27169                             tag : 'div',
27170                             cls : 'roo-upload-cropbox-empty-notify',
27171                             html : this.emptyText
27172                         },
27173                         {
27174                             tag : 'div',
27175                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27176                             html : this.rotateNotify
27177                         }
27178                     ]
27179                 },
27180                 {
27181                     tag : 'div',
27182                     cls : 'roo-upload-cropbox-footer',
27183                     cn : {
27184                         tag : 'div',
27185                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27186                         cn : []
27187                     }
27188                 }
27189             ]
27190         };
27191         
27192         return cfg;
27193     },
27194     
27195     onRender : function(ct, position)
27196     {
27197         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27198         
27199         if (this.buttons.length) {
27200             
27201             Roo.each(this.buttons, function(bb) {
27202                 
27203                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27204                 
27205                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27206                 
27207             }, this);
27208         }
27209         
27210         if(this.loadMask){
27211             this.maskEl = this.el;
27212         }
27213     },
27214     
27215     initEvents : function()
27216     {
27217         this.urlAPI = (window.createObjectURL && window) || 
27218                                 (window.URL && URL.revokeObjectURL && URL) || 
27219                                 (window.webkitURL && webkitURL);
27220                         
27221         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27222         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27223         
27224         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27225         this.selectorEl.hide();
27226         
27227         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27228         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27229         
27230         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27231         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27232         this.thumbEl.hide();
27233         
27234         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27235         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27236         
27237         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27238         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27239         this.errorEl.hide();
27240         
27241         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27242         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27243         this.footerEl.hide();
27244         
27245         this.setThumbBoxSize();
27246         
27247         this.bind();
27248         
27249         this.resize();
27250         
27251         this.fireEvent('initial', this);
27252     },
27253
27254     bind : function()
27255     {
27256         var _this = this;
27257         
27258         window.addEventListener("resize", function() { _this.resize(); } );
27259         
27260         this.bodyEl.on('click', this.beforeSelectFile, this);
27261         
27262         if(Roo.isTouch){
27263             this.bodyEl.on('touchstart', this.onTouchStart, this);
27264             this.bodyEl.on('touchmove', this.onTouchMove, this);
27265             this.bodyEl.on('touchend', this.onTouchEnd, this);
27266         }
27267         
27268         if(!Roo.isTouch){
27269             this.bodyEl.on('mousedown', this.onMouseDown, this);
27270             this.bodyEl.on('mousemove', this.onMouseMove, this);
27271             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27272             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27273             Roo.get(document).on('mouseup', this.onMouseUp, this);
27274         }
27275         
27276         this.selectorEl.on('change', this.onFileSelected, this);
27277     },
27278     
27279     reset : function()
27280     {    
27281         this.scale = 0;
27282         this.baseScale = 1;
27283         this.rotate = 0;
27284         this.baseRotate = 1;
27285         this.dragable = false;
27286         this.pinching = false;
27287         this.mouseX = 0;
27288         this.mouseY = 0;
27289         this.cropData = false;
27290         this.notifyEl.dom.innerHTML = this.emptyText;
27291         
27292         this.selectorEl.dom.value = '';
27293         
27294     },
27295     
27296     resize : function()
27297     {
27298         if(this.fireEvent('resize', this) != false){
27299             this.setThumbBoxPosition();
27300             this.setCanvasPosition();
27301         }
27302     },
27303     
27304     onFooterButtonClick : function(e, el, o, type)
27305     {
27306         switch (type) {
27307             case 'rotate-left' :
27308                 this.onRotateLeft(e);
27309                 break;
27310             case 'rotate-right' :
27311                 this.onRotateRight(e);
27312                 break;
27313             case 'picture' :
27314                 this.beforeSelectFile(e);
27315                 break;
27316             case 'trash' :
27317                 this.trash(e);
27318                 break;
27319             case 'crop' :
27320                 this.crop(e);
27321                 break;
27322             case 'download' :
27323                 this.download(e);
27324                 break;
27325             default :
27326                 break;
27327         }
27328         
27329         this.fireEvent('footerbuttonclick', this, type);
27330     },
27331     
27332     beforeSelectFile : function(e)
27333     {
27334         e.preventDefault();
27335         
27336         if(this.fireEvent('beforeselectfile', this) != false){
27337             this.selectorEl.dom.click();
27338         }
27339     },
27340     
27341     onFileSelected : function(e)
27342     {
27343         e.preventDefault();
27344         
27345         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27346             return;
27347         }
27348         
27349         var file = this.selectorEl.dom.files[0];
27350         
27351         if(this.fireEvent('inspect', this, file) != false){
27352             this.prepare(file);
27353         }
27354         
27355     },
27356     
27357     trash : function(e)
27358     {
27359         this.fireEvent('trash', this);
27360     },
27361     
27362     download : function(e)
27363     {
27364         this.fireEvent('download', this);
27365     },
27366     
27367     loadCanvas : function(src)
27368     {   
27369         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27370             
27371             this.reset();
27372             
27373             this.imageEl = document.createElement('img');
27374             
27375             var _this = this;
27376             
27377             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27378             
27379             this.imageEl.src = src;
27380         }
27381     },
27382     
27383     onLoadCanvas : function()
27384     {   
27385         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27386         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27387         
27388         this.bodyEl.un('click', this.beforeSelectFile, this);
27389         
27390         this.notifyEl.hide();
27391         this.thumbEl.show();
27392         this.footerEl.show();
27393         
27394         this.baseRotateLevel();
27395         
27396         if(this.isDocument){
27397             this.setThumbBoxSize();
27398         }
27399         
27400         this.setThumbBoxPosition();
27401         
27402         this.baseScaleLevel();
27403         
27404         this.draw();
27405         
27406         this.resize();
27407         
27408         this.canvasLoaded = true;
27409         
27410         if(this.loadMask){
27411             this.maskEl.unmask();
27412         }
27413         
27414     },
27415     
27416     setCanvasPosition : function()
27417     {   
27418         if(!this.canvasEl){
27419             return;
27420         }
27421         
27422         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27423         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27424         
27425         this.previewEl.setLeft(pw);
27426         this.previewEl.setTop(ph);
27427         
27428     },
27429     
27430     onMouseDown : function(e)
27431     {   
27432         e.stopEvent();
27433         
27434         this.dragable = true;
27435         this.pinching = false;
27436         
27437         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27438             this.dragable = false;
27439             return;
27440         }
27441         
27442         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27443         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27444         
27445     },
27446     
27447     onMouseMove : function(e)
27448     {   
27449         e.stopEvent();
27450         
27451         if(!this.canvasLoaded){
27452             return;
27453         }
27454         
27455         if (!this.dragable){
27456             return;
27457         }
27458         
27459         var minX = Math.ceil(this.thumbEl.getLeft(true));
27460         var minY = Math.ceil(this.thumbEl.getTop(true));
27461         
27462         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27463         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27464         
27465         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27466         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27467         
27468         x = x - this.mouseX;
27469         y = y - this.mouseY;
27470         
27471         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27472         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27473         
27474         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27475         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27476         
27477         this.previewEl.setLeft(bgX);
27478         this.previewEl.setTop(bgY);
27479         
27480         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27481         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27482     },
27483     
27484     onMouseUp : function(e)
27485     {   
27486         e.stopEvent();
27487         
27488         this.dragable = false;
27489     },
27490     
27491     onMouseWheel : function(e)
27492     {   
27493         e.stopEvent();
27494         
27495         this.startScale = this.scale;
27496         
27497         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27498         
27499         if(!this.zoomable()){
27500             this.scale = this.startScale;
27501             return;
27502         }
27503         
27504         this.draw();
27505         
27506         return;
27507     },
27508     
27509     zoomable : function()
27510     {
27511         var minScale = this.thumbEl.getWidth() / this.minWidth;
27512         
27513         if(this.minWidth < this.minHeight){
27514             minScale = this.thumbEl.getHeight() / this.minHeight;
27515         }
27516         
27517         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27518         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27519         
27520         if(
27521                 this.isDocument &&
27522                 (this.rotate == 0 || this.rotate == 180) && 
27523                 (
27524                     width > this.imageEl.OriginWidth || 
27525                     height > this.imageEl.OriginHeight ||
27526                     (width < this.minWidth && height < this.minHeight)
27527                 )
27528         ){
27529             return false;
27530         }
27531         
27532         if(
27533                 this.isDocument &&
27534                 (this.rotate == 90 || this.rotate == 270) && 
27535                 (
27536                     width > this.imageEl.OriginWidth || 
27537                     height > this.imageEl.OriginHeight ||
27538                     (width < this.minHeight && height < this.minWidth)
27539                 )
27540         ){
27541             return false;
27542         }
27543         
27544         if(
27545                 !this.isDocument &&
27546                 (this.rotate == 0 || this.rotate == 180) && 
27547                 (
27548                     width < this.minWidth || 
27549                     width > this.imageEl.OriginWidth || 
27550                     height < this.minHeight || 
27551                     height > this.imageEl.OriginHeight
27552                 )
27553         ){
27554             return false;
27555         }
27556         
27557         if(
27558                 !this.isDocument &&
27559                 (this.rotate == 90 || this.rotate == 270) && 
27560                 (
27561                     width < this.minHeight || 
27562                     width > this.imageEl.OriginWidth || 
27563                     height < this.minWidth || 
27564                     height > this.imageEl.OriginHeight
27565                 )
27566         ){
27567             return false;
27568         }
27569         
27570         return true;
27571         
27572     },
27573     
27574     onRotateLeft : function(e)
27575     {   
27576         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27577             
27578             var minScale = this.thumbEl.getWidth() / this.minWidth;
27579             
27580             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27581             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27582             
27583             this.startScale = this.scale;
27584             
27585             while (this.getScaleLevel() < minScale){
27586             
27587                 this.scale = this.scale + 1;
27588                 
27589                 if(!this.zoomable()){
27590                     break;
27591                 }
27592                 
27593                 if(
27594                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27595                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27596                 ){
27597                     continue;
27598                 }
27599                 
27600                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27601
27602                 this.draw();
27603                 
27604                 return;
27605             }
27606             
27607             this.scale = this.startScale;
27608             
27609             this.onRotateFail();
27610             
27611             return false;
27612         }
27613         
27614         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27615
27616         if(this.isDocument){
27617             this.setThumbBoxSize();
27618             this.setThumbBoxPosition();
27619             this.setCanvasPosition();
27620         }
27621         
27622         this.draw();
27623         
27624         this.fireEvent('rotate', this, 'left');
27625         
27626     },
27627     
27628     onRotateRight : function(e)
27629     {
27630         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27631             
27632             var minScale = this.thumbEl.getWidth() / this.minWidth;
27633         
27634             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27635             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27636             
27637             this.startScale = this.scale;
27638             
27639             while (this.getScaleLevel() < minScale){
27640             
27641                 this.scale = this.scale + 1;
27642                 
27643                 if(!this.zoomable()){
27644                     break;
27645                 }
27646                 
27647                 if(
27648                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27649                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27650                 ){
27651                     continue;
27652                 }
27653                 
27654                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27655
27656                 this.draw();
27657                 
27658                 return;
27659             }
27660             
27661             this.scale = this.startScale;
27662             
27663             this.onRotateFail();
27664             
27665             return false;
27666         }
27667         
27668         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27669
27670         if(this.isDocument){
27671             this.setThumbBoxSize();
27672             this.setThumbBoxPosition();
27673             this.setCanvasPosition();
27674         }
27675         
27676         this.draw();
27677         
27678         this.fireEvent('rotate', this, 'right');
27679     },
27680     
27681     onRotateFail : function()
27682     {
27683         this.errorEl.show(true);
27684         
27685         var _this = this;
27686         
27687         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27688     },
27689     
27690     draw : function()
27691     {
27692         this.previewEl.dom.innerHTML = '';
27693         
27694         var canvasEl = document.createElement("canvas");
27695         
27696         var contextEl = canvasEl.getContext("2d");
27697         
27698         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27699         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27700         var center = this.imageEl.OriginWidth / 2;
27701         
27702         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27703             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27704             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27705             center = this.imageEl.OriginHeight / 2;
27706         }
27707         
27708         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27709         
27710         contextEl.translate(center, center);
27711         contextEl.rotate(this.rotate * Math.PI / 180);
27712
27713         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27714         
27715         this.canvasEl = document.createElement("canvas");
27716         
27717         this.contextEl = this.canvasEl.getContext("2d");
27718         
27719         switch (this.rotate) {
27720             case 0 :
27721                 
27722                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27723                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27724                 
27725                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27726                 
27727                 break;
27728             case 90 : 
27729                 
27730                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27731                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27732                 
27733                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27734                     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);
27735                     break;
27736                 }
27737                 
27738                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27739                 
27740                 break;
27741             case 180 :
27742                 
27743                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27744                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27745                 
27746                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27747                     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);
27748                     break;
27749                 }
27750                 
27751                 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);
27752                 
27753                 break;
27754             case 270 :
27755                 
27756                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27757                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27758         
27759                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27760                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27761                     break;
27762                 }
27763                 
27764                 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);
27765                 
27766                 break;
27767             default : 
27768                 break;
27769         }
27770         
27771         this.previewEl.appendChild(this.canvasEl);
27772         
27773         this.setCanvasPosition();
27774     },
27775     
27776     crop : function()
27777     {
27778         if(!this.canvasLoaded){
27779             return;
27780         }
27781         
27782         var imageCanvas = document.createElement("canvas");
27783         
27784         var imageContext = imageCanvas.getContext("2d");
27785         
27786         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27787         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27788         
27789         var center = imageCanvas.width / 2;
27790         
27791         imageContext.translate(center, center);
27792         
27793         imageContext.rotate(this.rotate * Math.PI / 180);
27794         
27795         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27796         
27797         var canvas = document.createElement("canvas");
27798         
27799         var context = canvas.getContext("2d");
27800                 
27801         canvas.width = this.minWidth;
27802         canvas.height = this.minHeight;
27803
27804         switch (this.rotate) {
27805             case 0 :
27806                 
27807                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27808                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27809                 
27810                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27811                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27812                 
27813                 var targetWidth = this.minWidth - 2 * x;
27814                 var targetHeight = this.minHeight - 2 * y;
27815                 
27816                 var scale = 1;
27817                 
27818                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27819                     scale = targetWidth / width;
27820                 }
27821                 
27822                 if(x > 0 && y == 0){
27823                     scale = targetHeight / height;
27824                 }
27825                 
27826                 if(x > 0 && y > 0){
27827                     scale = targetWidth / width;
27828                     
27829                     if(width < height){
27830                         scale = targetHeight / height;
27831                     }
27832                 }
27833                 
27834                 context.scale(scale, scale);
27835                 
27836                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27837                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27838
27839                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27840                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27841
27842                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27843                 
27844                 break;
27845             case 90 : 
27846                 
27847                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27848                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27849                 
27850                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27851                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27852                 
27853                 var targetWidth = this.minWidth - 2 * x;
27854                 var targetHeight = this.minHeight - 2 * y;
27855                 
27856                 var scale = 1;
27857                 
27858                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27859                     scale = targetWidth / width;
27860                 }
27861                 
27862                 if(x > 0 && y == 0){
27863                     scale = targetHeight / height;
27864                 }
27865                 
27866                 if(x > 0 && y > 0){
27867                     scale = targetWidth / width;
27868                     
27869                     if(width < height){
27870                         scale = targetHeight / height;
27871                     }
27872                 }
27873                 
27874                 context.scale(scale, scale);
27875                 
27876                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27877                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27878
27879                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27880                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27881                 
27882                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27883                 
27884                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27885                 
27886                 break;
27887             case 180 :
27888                 
27889                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27890                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27891                 
27892                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27893                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27894                 
27895                 var targetWidth = this.minWidth - 2 * x;
27896                 var targetHeight = this.minHeight - 2 * y;
27897                 
27898                 var scale = 1;
27899                 
27900                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27901                     scale = targetWidth / width;
27902                 }
27903                 
27904                 if(x > 0 && y == 0){
27905                     scale = targetHeight / height;
27906                 }
27907                 
27908                 if(x > 0 && y > 0){
27909                     scale = targetWidth / width;
27910                     
27911                     if(width < height){
27912                         scale = targetHeight / height;
27913                     }
27914                 }
27915                 
27916                 context.scale(scale, scale);
27917                 
27918                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27919                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27920
27921                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27922                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27923
27924                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27925                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27926                 
27927                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27928                 
27929                 break;
27930             case 270 :
27931                 
27932                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27933                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27934                 
27935                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27936                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27937                 
27938                 var targetWidth = this.minWidth - 2 * x;
27939                 var targetHeight = this.minHeight - 2 * y;
27940                 
27941                 var scale = 1;
27942                 
27943                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27944                     scale = targetWidth / width;
27945                 }
27946                 
27947                 if(x > 0 && y == 0){
27948                     scale = targetHeight / height;
27949                 }
27950                 
27951                 if(x > 0 && y > 0){
27952                     scale = targetWidth / width;
27953                     
27954                     if(width < height){
27955                         scale = targetHeight / height;
27956                     }
27957                 }
27958                 
27959                 context.scale(scale, scale);
27960                 
27961                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27962                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27963
27964                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27965                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27966                 
27967                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27968                 
27969                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27970                 
27971                 break;
27972             default : 
27973                 break;
27974         }
27975         
27976         this.cropData = canvas.toDataURL(this.cropType);
27977         
27978         if(this.fireEvent('crop', this, this.cropData) !== false){
27979             this.process(this.file, this.cropData);
27980         }
27981         
27982         return;
27983         
27984     },
27985     
27986     setThumbBoxSize : function()
27987     {
27988         var width, height;
27989         
27990         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27991             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27992             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27993             
27994             this.minWidth = width;
27995             this.minHeight = height;
27996             
27997             if(this.rotate == 90 || this.rotate == 270){
27998                 this.minWidth = height;
27999                 this.minHeight = width;
28000             }
28001         }
28002         
28003         height = 300;
28004         width = Math.ceil(this.minWidth * height / this.minHeight);
28005         
28006         if(this.minWidth > this.minHeight){
28007             width = 300;
28008             height = Math.ceil(this.minHeight * width / this.minWidth);
28009         }
28010         
28011         this.thumbEl.setStyle({
28012             width : width + 'px',
28013             height : height + 'px'
28014         });
28015
28016         return;
28017             
28018     },
28019     
28020     setThumbBoxPosition : function()
28021     {
28022         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28023         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28024         
28025         this.thumbEl.setLeft(x);
28026         this.thumbEl.setTop(y);
28027         
28028     },
28029     
28030     baseRotateLevel : function()
28031     {
28032         this.baseRotate = 1;
28033         
28034         if(
28035                 typeof(this.exif) != 'undefined' &&
28036                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28037                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28038         ){
28039             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28040         }
28041         
28042         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28043         
28044     },
28045     
28046     baseScaleLevel : function()
28047     {
28048         var width, height;
28049         
28050         if(this.isDocument){
28051             
28052             if(this.baseRotate == 6 || this.baseRotate == 8){
28053             
28054                 height = this.thumbEl.getHeight();
28055                 this.baseScale = height / this.imageEl.OriginWidth;
28056
28057                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28058                     width = this.thumbEl.getWidth();
28059                     this.baseScale = width / this.imageEl.OriginHeight;
28060                 }
28061
28062                 return;
28063             }
28064
28065             height = this.thumbEl.getHeight();
28066             this.baseScale = height / this.imageEl.OriginHeight;
28067
28068             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28069                 width = this.thumbEl.getWidth();
28070                 this.baseScale = width / this.imageEl.OriginWidth;
28071             }
28072
28073             return;
28074         }
28075         
28076         if(this.baseRotate == 6 || this.baseRotate == 8){
28077             
28078             width = this.thumbEl.getHeight();
28079             this.baseScale = width / this.imageEl.OriginHeight;
28080             
28081             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28082                 height = this.thumbEl.getWidth();
28083                 this.baseScale = height / this.imageEl.OriginHeight;
28084             }
28085             
28086             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28087                 height = this.thumbEl.getWidth();
28088                 this.baseScale = height / this.imageEl.OriginHeight;
28089                 
28090                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28091                     width = this.thumbEl.getHeight();
28092                     this.baseScale = width / this.imageEl.OriginWidth;
28093                 }
28094             }
28095             
28096             return;
28097         }
28098         
28099         width = this.thumbEl.getWidth();
28100         this.baseScale = width / this.imageEl.OriginWidth;
28101         
28102         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28103             height = this.thumbEl.getHeight();
28104             this.baseScale = height / this.imageEl.OriginHeight;
28105         }
28106         
28107         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28108             
28109             height = this.thumbEl.getHeight();
28110             this.baseScale = height / this.imageEl.OriginHeight;
28111             
28112             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28113                 width = this.thumbEl.getWidth();
28114                 this.baseScale = width / this.imageEl.OriginWidth;
28115             }
28116             
28117         }
28118         
28119         return;
28120     },
28121     
28122     getScaleLevel : function()
28123     {
28124         return this.baseScale * Math.pow(1.1, this.scale);
28125     },
28126     
28127     onTouchStart : function(e)
28128     {
28129         if(!this.canvasLoaded){
28130             this.beforeSelectFile(e);
28131             return;
28132         }
28133         
28134         var touches = e.browserEvent.touches;
28135         
28136         if(!touches){
28137             return;
28138         }
28139         
28140         if(touches.length == 1){
28141             this.onMouseDown(e);
28142             return;
28143         }
28144         
28145         if(touches.length != 2){
28146             return;
28147         }
28148         
28149         var coords = [];
28150         
28151         for(var i = 0, finger; finger = touches[i]; i++){
28152             coords.push(finger.pageX, finger.pageY);
28153         }
28154         
28155         var x = Math.pow(coords[0] - coords[2], 2);
28156         var y = Math.pow(coords[1] - coords[3], 2);
28157         
28158         this.startDistance = Math.sqrt(x + y);
28159         
28160         this.startScale = this.scale;
28161         
28162         this.pinching = true;
28163         this.dragable = false;
28164         
28165     },
28166     
28167     onTouchMove : function(e)
28168     {
28169         if(!this.pinching && !this.dragable){
28170             return;
28171         }
28172         
28173         var touches = e.browserEvent.touches;
28174         
28175         if(!touches){
28176             return;
28177         }
28178         
28179         if(this.dragable){
28180             this.onMouseMove(e);
28181             return;
28182         }
28183         
28184         var coords = [];
28185         
28186         for(var i = 0, finger; finger = touches[i]; i++){
28187             coords.push(finger.pageX, finger.pageY);
28188         }
28189         
28190         var x = Math.pow(coords[0] - coords[2], 2);
28191         var y = Math.pow(coords[1] - coords[3], 2);
28192         
28193         this.endDistance = Math.sqrt(x + y);
28194         
28195         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28196         
28197         if(!this.zoomable()){
28198             this.scale = this.startScale;
28199             return;
28200         }
28201         
28202         this.draw();
28203         
28204     },
28205     
28206     onTouchEnd : function(e)
28207     {
28208         this.pinching = false;
28209         this.dragable = false;
28210         
28211     },
28212     
28213     process : function(file, crop)
28214     {
28215         if(this.loadMask){
28216             this.maskEl.mask(this.loadingText);
28217         }
28218         
28219         this.xhr = new XMLHttpRequest();
28220         
28221         file.xhr = this.xhr;
28222
28223         this.xhr.open(this.method, this.url, true);
28224         
28225         var headers = {
28226             "Accept": "application/json",
28227             "Cache-Control": "no-cache",
28228             "X-Requested-With": "XMLHttpRequest"
28229         };
28230         
28231         for (var headerName in headers) {
28232             var headerValue = headers[headerName];
28233             if (headerValue) {
28234                 this.xhr.setRequestHeader(headerName, headerValue);
28235             }
28236         }
28237         
28238         var _this = this;
28239         
28240         this.xhr.onload = function()
28241         {
28242             _this.xhrOnLoad(_this.xhr);
28243         }
28244         
28245         this.xhr.onerror = function()
28246         {
28247             _this.xhrOnError(_this.xhr);
28248         }
28249         
28250         var formData = new FormData();
28251
28252         formData.append('returnHTML', 'NO');
28253         
28254         if(crop){
28255             formData.append('crop', crop);
28256         }
28257         
28258         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28259             formData.append(this.paramName, file, file.name);
28260         }
28261         
28262         if(typeof(file.filename) != 'undefined'){
28263             formData.append('filename', file.filename);
28264         }
28265         
28266         if(typeof(file.mimetype) != 'undefined'){
28267             formData.append('mimetype', file.mimetype);
28268         }
28269         
28270         if(this.fireEvent('arrange', this, formData) != false){
28271             this.xhr.send(formData);
28272         };
28273     },
28274     
28275     xhrOnLoad : function(xhr)
28276     {
28277         if(this.loadMask){
28278             this.maskEl.unmask();
28279         }
28280         
28281         if (xhr.readyState !== 4) {
28282             this.fireEvent('exception', this, xhr);
28283             return;
28284         }
28285
28286         var response = Roo.decode(xhr.responseText);
28287         
28288         if(!response.success){
28289             this.fireEvent('exception', this, xhr);
28290             return;
28291         }
28292         
28293         var response = Roo.decode(xhr.responseText);
28294         
28295         this.fireEvent('upload', this, response);
28296         
28297     },
28298     
28299     xhrOnError : function()
28300     {
28301         if(this.loadMask){
28302             this.maskEl.unmask();
28303         }
28304         
28305         Roo.log('xhr on error');
28306         
28307         var response = Roo.decode(xhr.responseText);
28308           
28309         Roo.log(response);
28310         
28311     },
28312     
28313     prepare : function(file)
28314     {   
28315         if(this.loadMask){
28316             this.maskEl.mask(this.loadingText);
28317         }
28318         
28319         this.file = false;
28320         this.exif = {};
28321         
28322         if(typeof(file) === 'string'){
28323             this.loadCanvas(file);
28324             return;
28325         }
28326         
28327         if(!file || !this.urlAPI){
28328             return;
28329         }
28330         
28331         this.file = file;
28332         this.cropType = file.type;
28333         
28334         var _this = this;
28335         
28336         if(this.fireEvent('prepare', this, this.file) != false){
28337             
28338             var reader = new FileReader();
28339             
28340             reader.onload = function (e) {
28341                 if (e.target.error) {
28342                     Roo.log(e.target.error);
28343                     return;
28344                 }
28345                 
28346                 var buffer = e.target.result,
28347                     dataView = new DataView(buffer),
28348                     offset = 2,
28349                     maxOffset = dataView.byteLength - 4,
28350                     markerBytes,
28351                     markerLength;
28352                 
28353                 if (dataView.getUint16(0) === 0xffd8) {
28354                     while (offset < maxOffset) {
28355                         markerBytes = dataView.getUint16(offset);
28356                         
28357                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28358                             markerLength = dataView.getUint16(offset + 2) + 2;
28359                             if (offset + markerLength > dataView.byteLength) {
28360                                 Roo.log('Invalid meta data: Invalid segment size.');
28361                                 break;
28362                             }
28363                             
28364                             if(markerBytes == 0xffe1){
28365                                 _this.parseExifData(
28366                                     dataView,
28367                                     offset,
28368                                     markerLength
28369                                 );
28370                             }
28371                             
28372                             offset += markerLength;
28373                             
28374                             continue;
28375                         }
28376                         
28377                         break;
28378                     }
28379                     
28380                 }
28381                 
28382                 var url = _this.urlAPI.createObjectURL(_this.file);
28383                 
28384                 _this.loadCanvas(url);
28385                 
28386                 return;
28387             }
28388             
28389             reader.readAsArrayBuffer(this.file);
28390             
28391         }
28392         
28393     },
28394     
28395     parseExifData : function(dataView, offset, length)
28396     {
28397         var tiffOffset = offset + 10,
28398             littleEndian,
28399             dirOffset;
28400     
28401         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28402             // No Exif data, might be XMP data instead
28403             return;
28404         }
28405         
28406         // Check for the ASCII code for "Exif" (0x45786966):
28407         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28408             // No Exif data, might be XMP data instead
28409             return;
28410         }
28411         if (tiffOffset + 8 > dataView.byteLength) {
28412             Roo.log('Invalid Exif data: Invalid segment size.');
28413             return;
28414         }
28415         // Check for the two null bytes:
28416         if (dataView.getUint16(offset + 8) !== 0x0000) {
28417             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28418             return;
28419         }
28420         // Check the byte alignment:
28421         switch (dataView.getUint16(tiffOffset)) {
28422         case 0x4949:
28423             littleEndian = true;
28424             break;
28425         case 0x4D4D:
28426             littleEndian = false;
28427             break;
28428         default:
28429             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28430             return;
28431         }
28432         // Check for the TIFF tag marker (0x002A):
28433         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28434             Roo.log('Invalid Exif data: Missing TIFF marker.');
28435             return;
28436         }
28437         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28438         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28439         
28440         this.parseExifTags(
28441             dataView,
28442             tiffOffset,
28443             tiffOffset + dirOffset,
28444             littleEndian
28445         );
28446     },
28447     
28448     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28449     {
28450         var tagsNumber,
28451             dirEndOffset,
28452             i;
28453         if (dirOffset + 6 > dataView.byteLength) {
28454             Roo.log('Invalid Exif data: Invalid directory offset.');
28455             return;
28456         }
28457         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28458         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28459         if (dirEndOffset + 4 > dataView.byteLength) {
28460             Roo.log('Invalid Exif data: Invalid directory size.');
28461             return;
28462         }
28463         for (i = 0; i < tagsNumber; i += 1) {
28464             this.parseExifTag(
28465                 dataView,
28466                 tiffOffset,
28467                 dirOffset + 2 + 12 * i, // tag offset
28468                 littleEndian
28469             );
28470         }
28471         // Return the offset to the next directory:
28472         return dataView.getUint32(dirEndOffset, littleEndian);
28473     },
28474     
28475     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28476     {
28477         var tag = dataView.getUint16(offset, littleEndian);
28478         
28479         this.exif[tag] = this.getExifValue(
28480             dataView,
28481             tiffOffset,
28482             offset,
28483             dataView.getUint16(offset + 2, littleEndian), // tag type
28484             dataView.getUint32(offset + 4, littleEndian), // tag length
28485             littleEndian
28486         );
28487     },
28488     
28489     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28490     {
28491         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28492             tagSize,
28493             dataOffset,
28494             values,
28495             i,
28496             str,
28497             c;
28498     
28499         if (!tagType) {
28500             Roo.log('Invalid Exif data: Invalid tag type.');
28501             return;
28502         }
28503         
28504         tagSize = tagType.size * length;
28505         // Determine if the value is contained in the dataOffset bytes,
28506         // or if the value at the dataOffset is a pointer to the actual data:
28507         dataOffset = tagSize > 4 ?
28508                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28509         if (dataOffset + tagSize > dataView.byteLength) {
28510             Roo.log('Invalid Exif data: Invalid data offset.');
28511             return;
28512         }
28513         if (length === 1) {
28514             return tagType.getValue(dataView, dataOffset, littleEndian);
28515         }
28516         values = [];
28517         for (i = 0; i < length; i += 1) {
28518             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28519         }
28520         
28521         if (tagType.ascii) {
28522             str = '';
28523             // Concatenate the chars:
28524             for (i = 0; i < values.length; i += 1) {
28525                 c = values[i];
28526                 // Ignore the terminating NULL byte(s):
28527                 if (c === '\u0000') {
28528                     break;
28529                 }
28530                 str += c;
28531             }
28532             return str;
28533         }
28534         return values;
28535     }
28536     
28537 });
28538
28539 Roo.apply(Roo.bootstrap.UploadCropbox, {
28540     tags : {
28541         'Orientation': 0x0112
28542     },
28543     
28544     Orientation: {
28545             1: 0, //'top-left',
28546 //            2: 'top-right',
28547             3: 180, //'bottom-right',
28548 //            4: 'bottom-left',
28549 //            5: 'left-top',
28550             6: 90, //'right-top',
28551 //            7: 'right-bottom',
28552             8: 270 //'left-bottom'
28553     },
28554     
28555     exifTagTypes : {
28556         // byte, 8-bit unsigned int:
28557         1: {
28558             getValue: function (dataView, dataOffset) {
28559                 return dataView.getUint8(dataOffset);
28560             },
28561             size: 1
28562         },
28563         // ascii, 8-bit byte:
28564         2: {
28565             getValue: function (dataView, dataOffset) {
28566                 return String.fromCharCode(dataView.getUint8(dataOffset));
28567             },
28568             size: 1,
28569             ascii: true
28570         },
28571         // short, 16 bit int:
28572         3: {
28573             getValue: function (dataView, dataOffset, littleEndian) {
28574                 return dataView.getUint16(dataOffset, littleEndian);
28575             },
28576             size: 2
28577         },
28578         // long, 32 bit int:
28579         4: {
28580             getValue: function (dataView, dataOffset, littleEndian) {
28581                 return dataView.getUint32(dataOffset, littleEndian);
28582             },
28583             size: 4
28584         },
28585         // rational = two long values, first is numerator, second is denominator:
28586         5: {
28587             getValue: function (dataView, dataOffset, littleEndian) {
28588                 return dataView.getUint32(dataOffset, littleEndian) /
28589                     dataView.getUint32(dataOffset + 4, littleEndian);
28590             },
28591             size: 8
28592         },
28593         // slong, 32 bit signed int:
28594         9: {
28595             getValue: function (dataView, dataOffset, littleEndian) {
28596                 return dataView.getInt32(dataOffset, littleEndian);
28597             },
28598             size: 4
28599         },
28600         // srational, two slongs, first is numerator, second is denominator:
28601         10: {
28602             getValue: function (dataView, dataOffset, littleEndian) {
28603                 return dataView.getInt32(dataOffset, littleEndian) /
28604                     dataView.getInt32(dataOffset + 4, littleEndian);
28605             },
28606             size: 8
28607         }
28608     },
28609     
28610     footer : {
28611         STANDARD : [
28612             {
28613                 tag : 'div',
28614                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28615                 action : 'rotate-left',
28616                 cn : [
28617                     {
28618                         tag : 'button',
28619                         cls : 'btn btn-default',
28620                         html : '<i class="fa fa-undo"></i>'
28621                     }
28622                 ]
28623             },
28624             {
28625                 tag : 'div',
28626                 cls : 'btn-group roo-upload-cropbox-picture',
28627                 action : 'picture',
28628                 cn : [
28629                     {
28630                         tag : 'button',
28631                         cls : 'btn btn-default',
28632                         html : '<i class="fa fa-picture-o"></i>'
28633                     }
28634                 ]
28635             },
28636             {
28637                 tag : 'div',
28638                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28639                 action : 'rotate-right',
28640                 cn : [
28641                     {
28642                         tag : 'button',
28643                         cls : 'btn btn-default',
28644                         html : '<i class="fa fa-repeat"></i>'
28645                     }
28646                 ]
28647             }
28648         ],
28649         DOCUMENT : [
28650             {
28651                 tag : 'div',
28652                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28653                 action : 'rotate-left',
28654                 cn : [
28655                     {
28656                         tag : 'button',
28657                         cls : 'btn btn-default',
28658                         html : '<i class="fa fa-undo"></i>'
28659                     }
28660                 ]
28661             },
28662             {
28663                 tag : 'div',
28664                 cls : 'btn-group roo-upload-cropbox-download',
28665                 action : 'download',
28666                 cn : [
28667                     {
28668                         tag : 'button',
28669                         cls : 'btn btn-default',
28670                         html : '<i class="fa fa-download"></i>'
28671                     }
28672                 ]
28673             },
28674             {
28675                 tag : 'div',
28676                 cls : 'btn-group roo-upload-cropbox-crop',
28677                 action : 'crop',
28678                 cn : [
28679                     {
28680                         tag : 'button',
28681                         cls : 'btn btn-default',
28682                         html : '<i class="fa fa-crop"></i>'
28683                     }
28684                 ]
28685             },
28686             {
28687                 tag : 'div',
28688                 cls : 'btn-group roo-upload-cropbox-trash',
28689                 action : 'trash',
28690                 cn : [
28691                     {
28692                         tag : 'button',
28693                         cls : 'btn btn-default',
28694                         html : '<i class="fa fa-trash"></i>'
28695                     }
28696                 ]
28697             },
28698             {
28699                 tag : 'div',
28700                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28701                 action : 'rotate-right',
28702                 cn : [
28703                     {
28704                         tag : 'button',
28705                         cls : 'btn btn-default',
28706                         html : '<i class="fa fa-repeat"></i>'
28707                     }
28708                 ]
28709             }
28710         ],
28711         ROTATOR : [
28712             {
28713                 tag : 'div',
28714                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28715                 action : 'rotate-left',
28716                 cn : [
28717                     {
28718                         tag : 'button',
28719                         cls : 'btn btn-default',
28720                         html : '<i class="fa fa-undo"></i>'
28721                     }
28722                 ]
28723             },
28724             {
28725                 tag : 'div',
28726                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28727                 action : 'rotate-right',
28728                 cn : [
28729                     {
28730                         tag : 'button',
28731                         cls : 'btn btn-default',
28732                         html : '<i class="fa fa-repeat"></i>'
28733                     }
28734                 ]
28735             }
28736         ]
28737     }
28738 });
28739
28740 /*
28741 * Licence: LGPL
28742 */
28743
28744 /**
28745  * @class Roo.bootstrap.DocumentManager
28746  * @extends Roo.bootstrap.Component
28747  * Bootstrap DocumentManager class
28748  * @cfg {String} paramName default 'imageUpload'
28749  * @cfg {String} toolTipName default 'filename'
28750  * @cfg {String} method default POST
28751  * @cfg {String} url action url
28752  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28753  * @cfg {Boolean} multiple multiple upload default true
28754  * @cfg {Number} thumbSize default 300
28755  * @cfg {String} fieldLabel
28756  * @cfg {Number} labelWidth default 4
28757  * @cfg {String} labelAlign (left|top) default left
28758  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28759 * @cfg {Number} labellg set the width of label (1-12)
28760  * @cfg {Number} labelmd set the width of label (1-12)
28761  * @cfg {Number} labelsm set the width of label (1-12)
28762  * @cfg {Number} labelxs set the width of label (1-12)
28763  * 
28764  * @constructor
28765  * Create a new DocumentManager
28766  * @param {Object} config The config object
28767  */
28768
28769 Roo.bootstrap.DocumentManager = function(config){
28770     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28771     
28772     this.files = [];
28773     this.delegates = [];
28774     
28775     this.addEvents({
28776         /**
28777          * @event initial
28778          * Fire when initial the DocumentManager
28779          * @param {Roo.bootstrap.DocumentManager} this
28780          */
28781         "initial" : true,
28782         /**
28783          * @event inspect
28784          * inspect selected file
28785          * @param {Roo.bootstrap.DocumentManager} this
28786          * @param {File} file
28787          */
28788         "inspect" : true,
28789         /**
28790          * @event exception
28791          * Fire when xhr load exception
28792          * @param {Roo.bootstrap.DocumentManager} this
28793          * @param {XMLHttpRequest} xhr
28794          */
28795         "exception" : true,
28796         /**
28797          * @event afterupload
28798          * Fire when xhr load exception
28799          * @param {Roo.bootstrap.DocumentManager} this
28800          * @param {XMLHttpRequest} xhr
28801          */
28802         "afterupload" : true,
28803         /**
28804          * @event prepare
28805          * prepare the form data
28806          * @param {Roo.bootstrap.DocumentManager} this
28807          * @param {Object} formData
28808          */
28809         "prepare" : true,
28810         /**
28811          * @event remove
28812          * Fire when remove the file
28813          * @param {Roo.bootstrap.DocumentManager} this
28814          * @param {Object} file
28815          */
28816         "remove" : true,
28817         /**
28818          * @event refresh
28819          * Fire after refresh the file
28820          * @param {Roo.bootstrap.DocumentManager} this
28821          */
28822         "refresh" : true,
28823         /**
28824          * @event click
28825          * Fire after click the image
28826          * @param {Roo.bootstrap.DocumentManager} this
28827          * @param {Object} file
28828          */
28829         "click" : true,
28830         /**
28831          * @event edit
28832          * Fire when upload a image and editable set to true
28833          * @param {Roo.bootstrap.DocumentManager} this
28834          * @param {Object} file
28835          */
28836         "edit" : true,
28837         /**
28838          * @event beforeselectfile
28839          * Fire before select file
28840          * @param {Roo.bootstrap.DocumentManager} this
28841          */
28842         "beforeselectfile" : true,
28843         /**
28844          * @event process
28845          * Fire before process file
28846          * @param {Roo.bootstrap.DocumentManager} this
28847          * @param {Object} file
28848          */
28849         "process" : true,
28850         /**
28851          * @event previewrendered
28852          * Fire when preview rendered
28853          * @param {Roo.bootstrap.DocumentManager} this
28854          * @param {Object} file
28855          */
28856         "previewrendered" : true,
28857         /**
28858          */
28859         "previewResize" : true
28860         
28861     });
28862 };
28863
28864 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28865     
28866     boxes : 0,
28867     inputName : '',
28868     thumbSize : 300,
28869     multiple : true,
28870     files : false,
28871     method : 'POST',
28872     url : '',
28873     paramName : 'imageUpload',
28874     toolTipName : 'filename',
28875     fieldLabel : '',
28876     labelWidth : 4,
28877     labelAlign : 'left',
28878     editable : true,
28879     delegates : false,
28880     xhr : false, 
28881     
28882     labellg : 0,
28883     labelmd : 0,
28884     labelsm : 0,
28885     labelxs : 0,
28886     
28887     getAutoCreate : function()
28888     {   
28889         var managerWidget = {
28890             tag : 'div',
28891             cls : 'roo-document-manager',
28892             cn : [
28893                 {
28894                     tag : 'input',
28895                     cls : 'roo-document-manager-selector',
28896                     type : 'file'
28897                 },
28898                 {
28899                     tag : 'div',
28900                     cls : 'roo-document-manager-uploader',
28901                     cn : [
28902                         {
28903                             tag : 'div',
28904                             cls : 'roo-document-manager-upload-btn',
28905                             html : '<i class="fa fa-plus"></i>'
28906                         }
28907                     ]
28908                     
28909                 }
28910             ]
28911         };
28912         
28913         var content = [
28914             {
28915                 tag : 'div',
28916                 cls : 'column col-md-12',
28917                 cn : managerWidget
28918             }
28919         ];
28920         
28921         if(this.fieldLabel.length){
28922             
28923             content = [
28924                 {
28925                     tag : 'div',
28926                     cls : 'column col-md-12',
28927                     html : this.fieldLabel
28928                 },
28929                 {
28930                     tag : 'div',
28931                     cls : 'column col-md-12',
28932                     cn : managerWidget
28933                 }
28934             ];
28935
28936             if(this.labelAlign == 'left'){
28937                 content = [
28938                     {
28939                         tag : 'div',
28940                         cls : 'column',
28941                         html : this.fieldLabel
28942                     },
28943                     {
28944                         tag : 'div',
28945                         cls : 'column',
28946                         cn : managerWidget
28947                     }
28948                 ];
28949                 
28950                 if(this.labelWidth > 12){
28951                     content[0].style = "width: " + this.labelWidth + 'px';
28952                 }
28953
28954                 if(this.labelWidth < 13 && this.labelmd == 0){
28955                     this.labelmd = this.labelWidth;
28956                 }
28957
28958                 if(this.labellg > 0){
28959                     content[0].cls += ' col-lg-' + this.labellg;
28960                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28961                 }
28962
28963                 if(this.labelmd > 0){
28964                     content[0].cls += ' col-md-' + this.labelmd;
28965                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28966                 }
28967
28968                 if(this.labelsm > 0){
28969                     content[0].cls += ' col-sm-' + this.labelsm;
28970                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28971                 }
28972
28973                 if(this.labelxs > 0){
28974                     content[0].cls += ' col-xs-' + this.labelxs;
28975                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28976                 }
28977                 
28978             }
28979         }
28980         
28981         var cfg = {
28982             tag : 'div',
28983             cls : 'row clearfix',
28984             cn : content
28985         };
28986         
28987         return cfg;
28988         
28989     },
28990     
28991     initEvents : function()
28992     {
28993         this.managerEl = this.el.select('.roo-document-manager', true).first();
28994         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28995         
28996         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28997         this.selectorEl.hide();
28998         
28999         if(this.multiple){
29000             this.selectorEl.attr('multiple', 'multiple');
29001         }
29002         
29003         this.selectorEl.on('change', this.onFileSelected, this);
29004         
29005         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29006         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29007         
29008         this.uploader.on('click', this.onUploaderClick, this);
29009         
29010         this.renderProgressDialog();
29011         
29012         var _this = this;
29013         
29014         window.addEventListener("resize", function() { _this.refresh(); } );
29015         
29016         this.fireEvent('initial', this);
29017     },
29018     
29019     renderProgressDialog : function()
29020     {
29021         var _this = this;
29022         
29023         this.progressDialog = new Roo.bootstrap.Modal({
29024             cls : 'roo-document-manager-progress-dialog',
29025             allow_close : false,
29026             title : '',
29027             buttons : [
29028                 {
29029                     name  :'cancel',
29030                     weight : 'danger',
29031                     html : 'Cancel'
29032                 }
29033             ], 
29034             listeners : { 
29035                 btnclick : function() {
29036                     _this.uploadCancel();
29037                     this.hide();
29038                 }
29039             }
29040         });
29041          
29042         this.progressDialog.render(Roo.get(document.body));
29043          
29044         this.progress = new Roo.bootstrap.Progress({
29045             cls : 'roo-document-manager-progress',
29046             active : true,
29047             striped : true
29048         });
29049         
29050         this.progress.render(this.progressDialog.getChildContainer());
29051         
29052         this.progressBar = new Roo.bootstrap.ProgressBar({
29053             cls : 'roo-document-manager-progress-bar',
29054             aria_valuenow : 0,
29055             aria_valuemin : 0,
29056             aria_valuemax : 12,
29057             panel : 'success'
29058         });
29059         
29060         this.progressBar.render(this.progress.getChildContainer());
29061     },
29062     
29063     onUploaderClick : function(e)
29064     {
29065         e.preventDefault();
29066      
29067         if(this.fireEvent('beforeselectfile', this) != false){
29068             this.selectorEl.dom.click();
29069         }
29070         
29071     },
29072     
29073     onFileSelected : function(e)
29074     {
29075         e.preventDefault();
29076         
29077         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29078             return;
29079         }
29080         
29081         Roo.each(this.selectorEl.dom.files, function(file){
29082             if(this.fireEvent('inspect', this, file) != false){
29083                 this.files.push(file);
29084             }
29085         }, this);
29086         
29087         this.queue();
29088         
29089     },
29090     
29091     queue : function()
29092     {
29093         this.selectorEl.dom.value = '';
29094         
29095         if(!this.files || !this.files.length){
29096             return;
29097         }
29098         
29099         if(this.boxes > 0 && this.files.length > this.boxes){
29100             this.files = this.files.slice(0, this.boxes);
29101         }
29102         
29103         this.uploader.show();
29104         
29105         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29106             this.uploader.hide();
29107         }
29108         
29109         var _this = this;
29110         
29111         var files = [];
29112         
29113         var docs = [];
29114         
29115         Roo.each(this.files, function(file){
29116             
29117             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29118                 var f = this.renderPreview(file);
29119                 files.push(f);
29120                 return;
29121             }
29122             
29123             if(file.type.indexOf('image') != -1){
29124                 this.delegates.push(
29125                     (function(){
29126                         _this.process(file);
29127                     }).createDelegate(this)
29128                 );
29129         
29130                 return;
29131             }
29132             
29133             docs.push(
29134                 (function(){
29135                     _this.process(file);
29136                 }).createDelegate(this)
29137             );
29138             
29139         }, this);
29140         
29141         this.files = files;
29142         
29143         this.delegates = this.delegates.concat(docs);
29144         
29145         if(!this.delegates.length){
29146             this.refresh();
29147             return;
29148         }
29149         
29150         this.progressBar.aria_valuemax = this.delegates.length;
29151         
29152         this.arrange();
29153         
29154         return;
29155     },
29156     
29157     arrange : function()
29158     {
29159         if(!this.delegates.length){
29160             this.progressDialog.hide();
29161             this.refresh();
29162             return;
29163         }
29164         
29165         var delegate = this.delegates.shift();
29166         
29167         this.progressDialog.show();
29168         
29169         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29170         
29171         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29172         
29173         delegate();
29174     },
29175     
29176     refresh : function()
29177     {
29178         this.uploader.show();
29179         
29180         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29181             this.uploader.hide();
29182         }
29183         
29184         Roo.isTouch ? this.closable(false) : this.closable(true);
29185         
29186         this.fireEvent('refresh', this);
29187     },
29188     
29189     onRemove : function(e, el, o)
29190     {
29191         e.preventDefault();
29192         
29193         this.fireEvent('remove', this, o);
29194         
29195     },
29196     
29197     remove : function(o)
29198     {
29199         var files = [];
29200         
29201         Roo.each(this.files, function(file){
29202             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29203                 files.push(file);
29204                 return;
29205             }
29206
29207             o.target.remove();
29208
29209         }, this);
29210         
29211         this.files = files;
29212         
29213         this.refresh();
29214     },
29215     
29216     clear : function()
29217     {
29218         Roo.each(this.files, function(file){
29219             if(!file.target){
29220                 return;
29221             }
29222             
29223             file.target.remove();
29224
29225         }, this);
29226         
29227         this.files = [];
29228         
29229         this.refresh();
29230     },
29231     
29232     onClick : function(e, el, o)
29233     {
29234         e.preventDefault();
29235         
29236         this.fireEvent('click', this, o);
29237         
29238     },
29239     
29240     closable : function(closable)
29241     {
29242         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29243             
29244             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29245             
29246             if(closable){
29247                 el.show();
29248                 return;
29249             }
29250             
29251             el.hide();
29252             
29253         }, this);
29254     },
29255     
29256     xhrOnLoad : function(xhr)
29257     {
29258         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29259             el.remove();
29260         }, this);
29261         
29262         if (xhr.readyState !== 4) {
29263             this.arrange();
29264             this.fireEvent('exception', this, xhr);
29265             return;
29266         }
29267
29268         var response = Roo.decode(xhr.responseText);
29269         
29270         if(!response.success){
29271             this.arrange();
29272             this.fireEvent('exception', this, xhr);
29273             return;
29274         }
29275         
29276         var file = this.renderPreview(response.data);
29277         
29278         this.files.push(file);
29279         
29280         this.arrange();
29281         
29282         this.fireEvent('afterupload', this, xhr);
29283         
29284     },
29285     
29286     xhrOnError : function(xhr)
29287     {
29288         Roo.log('xhr on error');
29289         
29290         var response = Roo.decode(xhr.responseText);
29291           
29292         Roo.log(response);
29293         
29294         this.arrange();
29295     },
29296     
29297     process : function(file)
29298     {
29299         if(this.fireEvent('process', this, file) !== false){
29300             if(this.editable && file.type.indexOf('image') != -1){
29301                 this.fireEvent('edit', this, file);
29302                 return;
29303             }
29304
29305             this.uploadStart(file, false);
29306
29307             return;
29308         }
29309         
29310     },
29311     
29312     uploadStart : function(file, crop)
29313     {
29314         this.xhr = new XMLHttpRequest();
29315         
29316         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29317             this.arrange();
29318             return;
29319         }
29320         
29321         file.xhr = this.xhr;
29322             
29323         this.managerEl.createChild({
29324             tag : 'div',
29325             cls : 'roo-document-manager-loading',
29326             cn : [
29327                 {
29328                     tag : 'div',
29329                     tooltip : file.name,
29330                     cls : 'roo-document-manager-thumb',
29331                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29332                 }
29333             ]
29334
29335         });
29336
29337         this.xhr.open(this.method, this.url, true);
29338         
29339         var headers = {
29340             "Accept": "application/json",
29341             "Cache-Control": "no-cache",
29342             "X-Requested-With": "XMLHttpRequest"
29343         };
29344         
29345         for (var headerName in headers) {
29346             var headerValue = headers[headerName];
29347             if (headerValue) {
29348                 this.xhr.setRequestHeader(headerName, headerValue);
29349             }
29350         }
29351         
29352         var _this = this;
29353         
29354         this.xhr.onload = function()
29355         {
29356             _this.xhrOnLoad(_this.xhr);
29357         }
29358         
29359         this.xhr.onerror = function()
29360         {
29361             _this.xhrOnError(_this.xhr);
29362         }
29363         
29364         var formData = new FormData();
29365
29366         formData.append('returnHTML', 'NO');
29367         
29368         if(crop){
29369             formData.append('crop', crop);
29370         }
29371         
29372         formData.append(this.paramName, file, file.name);
29373         
29374         var options = {
29375             file : file, 
29376             manually : false
29377         };
29378         
29379         if(this.fireEvent('prepare', this, formData, options) != false){
29380             
29381             if(options.manually){
29382                 return;
29383             }
29384             
29385             this.xhr.send(formData);
29386             return;
29387         };
29388         
29389         this.uploadCancel();
29390     },
29391     
29392     uploadCancel : function()
29393     {
29394         if (this.xhr) {
29395             this.xhr.abort();
29396         }
29397         
29398         this.delegates = [];
29399         
29400         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29401             el.remove();
29402         }, this);
29403         
29404         this.arrange();
29405     },
29406     
29407     renderPreview : function(file)
29408     {
29409         if(typeof(file.target) != 'undefined' && file.target){
29410             return file;
29411         }
29412         
29413         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29414         
29415         var previewEl = this.managerEl.createChild({
29416             tag : 'div',
29417             cls : 'roo-document-manager-preview',
29418             cn : [
29419                 {
29420                     tag : 'div',
29421                     tooltip : file[this.toolTipName],
29422                     cls : 'roo-document-manager-thumb',
29423                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29424                 },
29425                 {
29426                     tag : 'button',
29427                     cls : 'close',
29428                     html : '<i class="fa fa-times-circle"></i>'
29429                 }
29430             ]
29431         });
29432
29433         var close = previewEl.select('button.close', true).first();
29434
29435         close.on('click', this.onRemove, this, file);
29436
29437         file.target = previewEl;
29438
29439         var image = previewEl.select('img', true).first();
29440         
29441         var _this = this;
29442         
29443         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29444         
29445         image.on('click', this.onClick, this, file);
29446         
29447         this.fireEvent('previewrendered', this, file);
29448         
29449         return file;
29450         
29451     },
29452     
29453     onPreviewLoad : function(file, image)
29454     {
29455         if(typeof(file.target) == 'undefined' || !file.target){
29456             return;
29457         }
29458         
29459         var width = image.dom.naturalWidth || image.dom.width;
29460         var height = image.dom.naturalHeight || image.dom.height;
29461         
29462         if(!this.previewResize) {
29463             return;
29464         }
29465         
29466         if(width > height){
29467             file.target.addClass('wide');
29468             return;
29469         }
29470         
29471         file.target.addClass('tall');
29472         return;
29473         
29474     },
29475     
29476     uploadFromSource : function(file, crop)
29477     {
29478         this.xhr = new XMLHttpRequest();
29479         
29480         this.managerEl.createChild({
29481             tag : 'div',
29482             cls : 'roo-document-manager-loading',
29483             cn : [
29484                 {
29485                     tag : 'div',
29486                     tooltip : file.name,
29487                     cls : 'roo-document-manager-thumb',
29488                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29489                 }
29490             ]
29491
29492         });
29493
29494         this.xhr.open(this.method, this.url, true);
29495         
29496         var headers = {
29497             "Accept": "application/json",
29498             "Cache-Control": "no-cache",
29499             "X-Requested-With": "XMLHttpRequest"
29500         };
29501         
29502         for (var headerName in headers) {
29503             var headerValue = headers[headerName];
29504             if (headerValue) {
29505                 this.xhr.setRequestHeader(headerName, headerValue);
29506             }
29507         }
29508         
29509         var _this = this;
29510         
29511         this.xhr.onload = function()
29512         {
29513             _this.xhrOnLoad(_this.xhr);
29514         }
29515         
29516         this.xhr.onerror = function()
29517         {
29518             _this.xhrOnError(_this.xhr);
29519         }
29520         
29521         var formData = new FormData();
29522
29523         formData.append('returnHTML', 'NO');
29524         
29525         formData.append('crop', crop);
29526         
29527         if(typeof(file.filename) != 'undefined'){
29528             formData.append('filename', file.filename);
29529         }
29530         
29531         if(typeof(file.mimetype) != 'undefined'){
29532             formData.append('mimetype', file.mimetype);
29533         }
29534         
29535         Roo.log(formData);
29536         
29537         if(this.fireEvent('prepare', this, formData) != false){
29538             this.xhr.send(formData);
29539         };
29540     }
29541 });
29542
29543 /*
29544 * Licence: LGPL
29545 */
29546
29547 /**
29548  * @class Roo.bootstrap.DocumentViewer
29549  * @extends Roo.bootstrap.Component
29550  * Bootstrap DocumentViewer class
29551  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29552  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29553  * 
29554  * @constructor
29555  * Create a new DocumentViewer
29556  * @param {Object} config The config object
29557  */
29558
29559 Roo.bootstrap.DocumentViewer = function(config){
29560     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29561     
29562     this.addEvents({
29563         /**
29564          * @event initial
29565          * Fire after initEvent
29566          * @param {Roo.bootstrap.DocumentViewer} this
29567          */
29568         "initial" : true,
29569         /**
29570          * @event click
29571          * Fire after click
29572          * @param {Roo.bootstrap.DocumentViewer} this
29573          */
29574         "click" : true,
29575         /**
29576          * @event download
29577          * Fire after download button
29578          * @param {Roo.bootstrap.DocumentViewer} this
29579          */
29580         "download" : true,
29581         /**
29582          * @event trash
29583          * Fire after trash button
29584          * @param {Roo.bootstrap.DocumentViewer} this
29585          */
29586         "trash" : true
29587         
29588     });
29589 };
29590
29591 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29592     
29593     showDownload : true,
29594     
29595     showTrash : true,
29596     
29597     getAutoCreate : function()
29598     {
29599         var cfg = {
29600             tag : 'div',
29601             cls : 'roo-document-viewer',
29602             cn : [
29603                 {
29604                     tag : 'div',
29605                     cls : 'roo-document-viewer-body',
29606                     cn : [
29607                         {
29608                             tag : 'div',
29609                             cls : 'roo-document-viewer-thumb',
29610                             cn : [
29611                                 {
29612                                     tag : 'img',
29613                                     cls : 'roo-document-viewer-image'
29614                                 }
29615                             ]
29616                         }
29617                     ]
29618                 },
29619                 {
29620                     tag : 'div',
29621                     cls : 'roo-document-viewer-footer',
29622                     cn : {
29623                         tag : 'div',
29624                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29625                         cn : [
29626                             {
29627                                 tag : 'div',
29628                                 cls : 'btn-group roo-document-viewer-download',
29629                                 cn : [
29630                                     {
29631                                         tag : 'button',
29632                                         cls : 'btn btn-default',
29633                                         html : '<i class="fa fa-download"></i>'
29634                                     }
29635                                 ]
29636                             },
29637                             {
29638                                 tag : 'div',
29639                                 cls : 'btn-group roo-document-viewer-trash',
29640                                 cn : [
29641                                     {
29642                                         tag : 'button',
29643                                         cls : 'btn btn-default',
29644                                         html : '<i class="fa fa-trash"></i>'
29645                                     }
29646                                 ]
29647                             }
29648                         ]
29649                     }
29650                 }
29651             ]
29652         };
29653         
29654         return cfg;
29655     },
29656     
29657     initEvents : function()
29658     {
29659         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29660         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29661         
29662         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29663         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29664         
29665         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29666         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29667         
29668         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29669         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29670         
29671         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29672         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29673         
29674         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29675         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29676         
29677         this.bodyEl.on('click', this.onClick, this);
29678         this.downloadBtn.on('click', this.onDownload, this);
29679         this.trashBtn.on('click', this.onTrash, this);
29680         
29681         this.downloadBtn.hide();
29682         this.trashBtn.hide();
29683         
29684         if(this.showDownload){
29685             this.downloadBtn.show();
29686         }
29687         
29688         if(this.showTrash){
29689             this.trashBtn.show();
29690         }
29691         
29692         if(!this.showDownload && !this.showTrash) {
29693             this.footerEl.hide();
29694         }
29695         
29696     },
29697     
29698     initial : function()
29699     {
29700         this.fireEvent('initial', this);
29701         
29702     },
29703     
29704     onClick : function(e)
29705     {
29706         e.preventDefault();
29707         
29708         this.fireEvent('click', this);
29709     },
29710     
29711     onDownload : function(e)
29712     {
29713         e.preventDefault();
29714         
29715         this.fireEvent('download', this);
29716     },
29717     
29718     onTrash : function(e)
29719     {
29720         e.preventDefault();
29721         
29722         this.fireEvent('trash', this);
29723     }
29724     
29725 });
29726 /*
29727  * - LGPL
29728  *
29729  * nav progress bar
29730  * 
29731  */
29732
29733 /**
29734  * @class Roo.bootstrap.NavProgressBar
29735  * @extends Roo.bootstrap.Component
29736  * Bootstrap NavProgressBar class
29737  * 
29738  * @constructor
29739  * Create a new nav progress bar
29740  * @param {Object} config The config object
29741  */
29742
29743 Roo.bootstrap.NavProgressBar = function(config){
29744     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29745
29746     this.bullets = this.bullets || [];
29747    
29748 //    Roo.bootstrap.NavProgressBar.register(this);
29749      this.addEvents({
29750         /**
29751              * @event changed
29752              * Fires when the active item changes
29753              * @param {Roo.bootstrap.NavProgressBar} this
29754              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29755              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29756          */
29757         'changed': true
29758      });
29759     
29760 };
29761
29762 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29763     
29764     bullets : [],
29765     barItems : [],
29766     
29767     getAutoCreate : function()
29768     {
29769         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29770         
29771         cfg = {
29772             tag : 'div',
29773             cls : 'roo-navigation-bar-group',
29774             cn : [
29775                 {
29776                     tag : 'div',
29777                     cls : 'roo-navigation-top-bar'
29778                 },
29779                 {
29780                     tag : 'div',
29781                     cls : 'roo-navigation-bullets-bar',
29782                     cn : [
29783                         {
29784                             tag : 'ul',
29785                             cls : 'roo-navigation-bar'
29786                         }
29787                     ]
29788                 },
29789                 
29790                 {
29791                     tag : 'div',
29792                     cls : 'roo-navigation-bottom-bar'
29793                 }
29794             ]
29795             
29796         };
29797         
29798         return cfg;
29799         
29800     },
29801     
29802     initEvents: function() 
29803     {
29804         
29805     },
29806     
29807     onRender : function(ct, position) 
29808     {
29809         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29810         
29811         if(this.bullets.length){
29812             Roo.each(this.bullets, function(b){
29813                this.addItem(b);
29814             }, this);
29815         }
29816         
29817         this.format();
29818         
29819     },
29820     
29821     addItem : function(cfg)
29822     {
29823         var item = new Roo.bootstrap.NavProgressItem(cfg);
29824         
29825         item.parentId = this.id;
29826         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29827         
29828         if(cfg.html){
29829             var top = new Roo.bootstrap.Element({
29830                 tag : 'div',
29831                 cls : 'roo-navigation-bar-text'
29832             });
29833             
29834             var bottom = new Roo.bootstrap.Element({
29835                 tag : 'div',
29836                 cls : 'roo-navigation-bar-text'
29837             });
29838             
29839             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29840             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29841             
29842             var topText = new Roo.bootstrap.Element({
29843                 tag : 'span',
29844                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29845             });
29846             
29847             var bottomText = new Roo.bootstrap.Element({
29848                 tag : 'span',
29849                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29850             });
29851             
29852             topText.onRender(top.el, null);
29853             bottomText.onRender(bottom.el, null);
29854             
29855             item.topEl = top;
29856             item.bottomEl = bottom;
29857         }
29858         
29859         this.barItems.push(item);
29860         
29861         return item;
29862     },
29863     
29864     getActive : function()
29865     {
29866         var active = false;
29867         
29868         Roo.each(this.barItems, function(v){
29869             
29870             if (!v.isActive()) {
29871                 return;
29872             }
29873             
29874             active = v;
29875             return false;
29876             
29877         });
29878         
29879         return active;
29880     },
29881     
29882     setActiveItem : function(item)
29883     {
29884         var prev = false;
29885         
29886         Roo.each(this.barItems, function(v){
29887             if (v.rid == item.rid) {
29888                 return ;
29889             }
29890             
29891             if (v.isActive()) {
29892                 v.setActive(false);
29893                 prev = v;
29894             }
29895         });
29896
29897         item.setActive(true);
29898         
29899         this.fireEvent('changed', this, item, prev);
29900     },
29901     
29902     getBarItem: function(rid)
29903     {
29904         var ret = false;
29905         
29906         Roo.each(this.barItems, function(e) {
29907             if (e.rid != rid) {
29908                 return;
29909             }
29910             
29911             ret =  e;
29912             return false;
29913         });
29914         
29915         return ret;
29916     },
29917     
29918     indexOfItem : function(item)
29919     {
29920         var index = false;
29921         
29922         Roo.each(this.barItems, function(v, i){
29923             
29924             if (v.rid != item.rid) {
29925                 return;
29926             }
29927             
29928             index = i;
29929             return false
29930         });
29931         
29932         return index;
29933     },
29934     
29935     setActiveNext : function()
29936     {
29937         var i = this.indexOfItem(this.getActive());
29938         
29939         if (i > this.barItems.length) {
29940             return;
29941         }
29942         
29943         this.setActiveItem(this.barItems[i+1]);
29944     },
29945     
29946     setActivePrev : function()
29947     {
29948         var i = this.indexOfItem(this.getActive());
29949         
29950         if (i  < 1) {
29951             return;
29952         }
29953         
29954         this.setActiveItem(this.barItems[i-1]);
29955     },
29956     
29957     format : function()
29958     {
29959         if(!this.barItems.length){
29960             return;
29961         }
29962      
29963         var width = 100 / this.barItems.length;
29964         
29965         Roo.each(this.barItems, function(i){
29966             i.el.setStyle('width', width + '%');
29967             i.topEl.el.setStyle('width', width + '%');
29968             i.bottomEl.el.setStyle('width', width + '%');
29969         }, this);
29970         
29971     }
29972     
29973 });
29974 /*
29975  * - LGPL
29976  *
29977  * Nav Progress Item
29978  * 
29979  */
29980
29981 /**
29982  * @class Roo.bootstrap.NavProgressItem
29983  * @extends Roo.bootstrap.Component
29984  * Bootstrap NavProgressItem class
29985  * @cfg {String} rid the reference id
29986  * @cfg {Boolean} active (true|false) Is item active default false
29987  * @cfg {Boolean} disabled (true|false) Is item active default false
29988  * @cfg {String} html
29989  * @cfg {String} position (top|bottom) text position default bottom
29990  * @cfg {String} icon show icon instead of number
29991  * 
29992  * @constructor
29993  * Create a new NavProgressItem
29994  * @param {Object} config The config object
29995  */
29996 Roo.bootstrap.NavProgressItem = function(config){
29997     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29998     this.addEvents({
29999         // raw events
30000         /**
30001          * @event click
30002          * The raw click event for the entire grid.
30003          * @param {Roo.bootstrap.NavProgressItem} this
30004          * @param {Roo.EventObject} e
30005          */
30006         "click" : true
30007     });
30008    
30009 };
30010
30011 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30012     
30013     rid : '',
30014     active : false,
30015     disabled : false,
30016     html : '',
30017     position : 'bottom',
30018     icon : false,
30019     
30020     getAutoCreate : function()
30021     {
30022         var iconCls = 'roo-navigation-bar-item-icon';
30023         
30024         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30025         
30026         var cfg = {
30027             tag: 'li',
30028             cls: 'roo-navigation-bar-item',
30029             cn : [
30030                 {
30031                     tag : 'i',
30032                     cls : iconCls
30033                 }
30034             ]
30035         };
30036         
30037         if(this.active){
30038             cfg.cls += ' active';
30039         }
30040         if(this.disabled){
30041             cfg.cls += ' disabled';
30042         }
30043         
30044         return cfg;
30045     },
30046     
30047     disable : function()
30048     {
30049         this.setDisabled(true);
30050     },
30051     
30052     enable : function()
30053     {
30054         this.setDisabled(false);
30055     },
30056     
30057     initEvents: function() 
30058     {
30059         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30060         
30061         this.iconEl.on('click', this.onClick, this);
30062     },
30063     
30064     onClick : function(e)
30065     {
30066         e.preventDefault();
30067         
30068         if(this.disabled){
30069             return;
30070         }
30071         
30072         if(this.fireEvent('click', this, e) === false){
30073             return;
30074         };
30075         
30076         this.parent().setActiveItem(this);
30077     },
30078     
30079     isActive: function () 
30080     {
30081         return this.active;
30082     },
30083     
30084     setActive : function(state)
30085     {
30086         if(this.active == state){
30087             return;
30088         }
30089         
30090         this.active = state;
30091         
30092         if (state) {
30093             this.el.addClass('active');
30094             return;
30095         }
30096         
30097         this.el.removeClass('active');
30098         
30099         return;
30100     },
30101     
30102     setDisabled : function(state)
30103     {
30104         if(this.disabled == state){
30105             return;
30106         }
30107         
30108         this.disabled = state;
30109         
30110         if (state) {
30111             this.el.addClass('disabled');
30112             return;
30113         }
30114         
30115         this.el.removeClass('disabled');
30116     },
30117     
30118     tooltipEl : function()
30119     {
30120         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30121     }
30122 });
30123  
30124
30125  /*
30126  * - LGPL
30127  *
30128  * FieldLabel
30129  * 
30130  */
30131
30132 /**
30133  * @class Roo.bootstrap.FieldLabel
30134  * @extends Roo.bootstrap.Component
30135  * Bootstrap FieldLabel class
30136  * @cfg {String} html contents of the element
30137  * @cfg {String} tag tag of the element default label
30138  * @cfg {String} cls class of the element
30139  * @cfg {String} target label target 
30140  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30141  * @cfg {String} invalidClass default "text-warning"
30142  * @cfg {String} validClass default "text-success"
30143  * @cfg {String} iconTooltip default "This field is required"
30144  * @cfg {String} indicatorpos (left|right) default left
30145  * 
30146  * @constructor
30147  * Create a new FieldLabel
30148  * @param {Object} config The config object
30149  */
30150
30151 Roo.bootstrap.FieldLabel = function(config){
30152     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30153     
30154     this.addEvents({
30155             /**
30156              * @event invalid
30157              * Fires after the field has been marked as invalid.
30158              * @param {Roo.form.FieldLabel} this
30159              * @param {String} msg The validation message
30160              */
30161             invalid : true,
30162             /**
30163              * @event valid
30164              * Fires after the field has been validated with no errors.
30165              * @param {Roo.form.FieldLabel} this
30166              */
30167             valid : true
30168         });
30169 };
30170
30171 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30172     
30173     tag: 'label',
30174     cls: '',
30175     html: '',
30176     target: '',
30177     allowBlank : true,
30178     invalidClass : 'has-warning',
30179     validClass : 'has-success',
30180     iconTooltip : 'This field is required',
30181     indicatorpos : 'left',
30182     
30183     getAutoCreate : function(){
30184         
30185         var cls = "";
30186         if (!this.allowBlank) {
30187             cls  = "visible";
30188         }
30189         
30190         var cfg = {
30191             tag : this.tag,
30192             cls : 'roo-bootstrap-field-label ' + this.cls,
30193             for : this.target,
30194             cn : [
30195                 {
30196                     tag : 'i',
30197                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30198                     tooltip : this.iconTooltip
30199                 },
30200                 {
30201                     tag : 'span',
30202                     html : this.html
30203                 }
30204             ] 
30205         };
30206         
30207         if(this.indicatorpos == 'right'){
30208             var cfg = {
30209                 tag : this.tag,
30210                 cls : 'roo-bootstrap-field-label ' + this.cls,
30211                 for : this.target,
30212                 cn : [
30213                     {
30214                         tag : 'span',
30215                         html : this.html
30216                     },
30217                     {
30218                         tag : 'i',
30219                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30220                         tooltip : this.iconTooltip
30221                     }
30222                 ] 
30223             };
30224         }
30225         
30226         return cfg;
30227     },
30228     
30229     initEvents: function() 
30230     {
30231         Roo.bootstrap.Element.superclass.initEvents.call(this);
30232         
30233         this.indicator = this.indicatorEl();
30234         
30235         if(this.indicator){
30236             this.indicator.removeClass('visible');
30237             this.indicator.addClass('invisible');
30238         }
30239         
30240         Roo.bootstrap.FieldLabel.register(this);
30241     },
30242     
30243     indicatorEl : function()
30244     {
30245         var indicator = this.el.select('i.roo-required-indicator',true).first();
30246         
30247         if(!indicator){
30248             return false;
30249         }
30250         
30251         return indicator;
30252         
30253     },
30254     
30255     /**
30256      * Mark this field as valid
30257      */
30258     markValid : function()
30259     {
30260         if(this.indicator){
30261             this.indicator.removeClass('visible');
30262             this.indicator.addClass('invisible');
30263         }
30264         
30265         this.el.removeClass(this.invalidClass);
30266         
30267         this.el.addClass(this.validClass);
30268         
30269         this.fireEvent('valid', this);
30270     },
30271     
30272     /**
30273      * Mark this field as invalid
30274      * @param {String} msg The validation message
30275      */
30276     markInvalid : function(msg)
30277     {
30278         if(this.indicator){
30279             this.indicator.removeClass('invisible');
30280             this.indicator.addClass('visible');
30281         }
30282         
30283         this.el.removeClass(this.validClass);
30284         
30285         this.el.addClass(this.invalidClass);
30286         
30287         this.fireEvent('invalid', this, msg);
30288     }
30289     
30290    
30291 });
30292
30293 Roo.apply(Roo.bootstrap.FieldLabel, {
30294     
30295     groups: {},
30296     
30297      /**
30298     * register a FieldLabel Group
30299     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30300     */
30301     register : function(label)
30302     {
30303         if(this.groups.hasOwnProperty(label.target)){
30304             return;
30305         }
30306      
30307         this.groups[label.target] = label;
30308         
30309     },
30310     /**
30311     * fetch a FieldLabel Group based on the target
30312     * @param {string} target
30313     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30314     */
30315     get: function(target) {
30316         if (typeof(this.groups[target]) == 'undefined') {
30317             return false;
30318         }
30319         
30320         return this.groups[target] ;
30321     }
30322 });
30323
30324  
30325
30326  /*
30327  * - LGPL
30328  *
30329  * page DateSplitField.
30330  * 
30331  */
30332
30333
30334 /**
30335  * @class Roo.bootstrap.DateSplitField
30336  * @extends Roo.bootstrap.Component
30337  * Bootstrap DateSplitField class
30338  * @cfg {string} fieldLabel - the label associated
30339  * @cfg {Number} labelWidth set the width of label (0-12)
30340  * @cfg {String} labelAlign (top|left)
30341  * @cfg {Boolean} dayAllowBlank (true|false) default false
30342  * @cfg {Boolean} monthAllowBlank (true|false) default false
30343  * @cfg {Boolean} yearAllowBlank (true|false) default false
30344  * @cfg {string} dayPlaceholder 
30345  * @cfg {string} monthPlaceholder
30346  * @cfg {string} yearPlaceholder
30347  * @cfg {string} dayFormat default 'd'
30348  * @cfg {string} monthFormat default 'm'
30349  * @cfg {string} yearFormat default 'Y'
30350  * @cfg {Number} labellg set the width of label (1-12)
30351  * @cfg {Number} labelmd set the width of label (1-12)
30352  * @cfg {Number} labelsm set the width of label (1-12)
30353  * @cfg {Number} labelxs set the width of label (1-12)
30354
30355  *     
30356  * @constructor
30357  * Create a new DateSplitField
30358  * @param {Object} config The config object
30359  */
30360
30361 Roo.bootstrap.DateSplitField = function(config){
30362     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30363     
30364     this.addEvents({
30365         // raw events
30366          /**
30367          * @event years
30368          * getting the data of years
30369          * @param {Roo.bootstrap.DateSplitField} this
30370          * @param {Object} years
30371          */
30372         "years" : true,
30373         /**
30374          * @event days
30375          * getting the data of days
30376          * @param {Roo.bootstrap.DateSplitField} this
30377          * @param {Object} days
30378          */
30379         "days" : true,
30380         /**
30381          * @event invalid
30382          * Fires after the field has been marked as invalid.
30383          * @param {Roo.form.Field} this
30384          * @param {String} msg The validation message
30385          */
30386         invalid : true,
30387        /**
30388          * @event valid
30389          * Fires after the field has been validated with no errors.
30390          * @param {Roo.form.Field} this
30391          */
30392         valid : true
30393     });
30394 };
30395
30396 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30397     
30398     fieldLabel : '',
30399     labelAlign : 'top',
30400     labelWidth : 3,
30401     dayAllowBlank : false,
30402     monthAllowBlank : false,
30403     yearAllowBlank : false,
30404     dayPlaceholder : '',
30405     monthPlaceholder : '',
30406     yearPlaceholder : '',
30407     dayFormat : 'd',
30408     monthFormat : 'm',
30409     yearFormat : 'Y',
30410     isFormField : true,
30411     labellg : 0,
30412     labelmd : 0,
30413     labelsm : 0,
30414     labelxs : 0,
30415     
30416     getAutoCreate : function()
30417     {
30418         var cfg = {
30419             tag : 'div',
30420             cls : 'row roo-date-split-field-group',
30421             cn : [
30422                 {
30423                     tag : 'input',
30424                     type : 'hidden',
30425                     cls : 'form-hidden-field roo-date-split-field-group-value',
30426                     name : this.name
30427                 }
30428             ]
30429         };
30430         
30431         var labelCls = 'col-md-12';
30432         var contentCls = 'col-md-4';
30433         
30434         if(this.fieldLabel){
30435             
30436             var label = {
30437                 tag : 'div',
30438                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30439                 cn : [
30440                     {
30441                         tag : 'label',
30442                         html : this.fieldLabel
30443                     }
30444                 ]
30445             };
30446             
30447             if(this.labelAlign == 'left'){
30448             
30449                 if(this.labelWidth > 12){
30450                     label.style = "width: " + this.labelWidth + 'px';
30451                 }
30452
30453                 if(this.labelWidth < 13 && this.labelmd == 0){
30454                     this.labelmd = this.labelWidth;
30455                 }
30456
30457                 if(this.labellg > 0){
30458                     labelCls = ' col-lg-' + this.labellg;
30459                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30460                 }
30461
30462                 if(this.labelmd > 0){
30463                     labelCls = ' col-md-' + this.labelmd;
30464                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30465                 }
30466
30467                 if(this.labelsm > 0){
30468                     labelCls = ' col-sm-' + this.labelsm;
30469                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30470                 }
30471
30472                 if(this.labelxs > 0){
30473                     labelCls = ' col-xs-' + this.labelxs;
30474                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30475                 }
30476             }
30477             
30478             label.cls += ' ' + labelCls;
30479             
30480             cfg.cn.push(label);
30481         }
30482         
30483         Roo.each(['day', 'month', 'year'], function(t){
30484             cfg.cn.push({
30485                 tag : 'div',
30486                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30487             });
30488         }, this);
30489         
30490         return cfg;
30491     },
30492     
30493     inputEl: function ()
30494     {
30495         return this.el.select('.roo-date-split-field-group-value', true).first();
30496     },
30497     
30498     onRender : function(ct, position) 
30499     {
30500         var _this = this;
30501         
30502         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30503         
30504         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30505         
30506         this.dayField = new Roo.bootstrap.ComboBox({
30507             allowBlank : this.dayAllowBlank,
30508             alwaysQuery : true,
30509             displayField : 'value',
30510             editable : false,
30511             fieldLabel : '',
30512             forceSelection : true,
30513             mode : 'local',
30514             placeholder : this.dayPlaceholder,
30515             selectOnFocus : true,
30516             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30517             triggerAction : 'all',
30518             typeAhead : true,
30519             valueField : 'value',
30520             store : new Roo.data.SimpleStore({
30521                 data : (function() {    
30522                     var days = [];
30523                     _this.fireEvent('days', _this, days);
30524                     return days;
30525                 })(),
30526                 fields : [ 'value' ]
30527             }),
30528             listeners : {
30529                 select : function (_self, record, index)
30530                 {
30531                     _this.setValue(_this.getValue());
30532                 }
30533             }
30534         });
30535
30536         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30537         
30538         this.monthField = new Roo.bootstrap.MonthField({
30539             after : '<i class=\"fa fa-calendar\"></i>',
30540             allowBlank : this.monthAllowBlank,
30541             placeholder : this.monthPlaceholder,
30542             readOnly : true,
30543             listeners : {
30544                 render : function (_self)
30545                 {
30546                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30547                         e.preventDefault();
30548                         _self.focus();
30549                     });
30550                 },
30551                 select : function (_self, oldvalue, newvalue)
30552                 {
30553                     _this.setValue(_this.getValue());
30554                 }
30555             }
30556         });
30557         
30558         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30559         
30560         this.yearField = new Roo.bootstrap.ComboBox({
30561             allowBlank : this.yearAllowBlank,
30562             alwaysQuery : true,
30563             displayField : 'value',
30564             editable : false,
30565             fieldLabel : '',
30566             forceSelection : true,
30567             mode : 'local',
30568             placeholder : this.yearPlaceholder,
30569             selectOnFocus : true,
30570             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30571             triggerAction : 'all',
30572             typeAhead : true,
30573             valueField : 'value',
30574             store : new Roo.data.SimpleStore({
30575                 data : (function() {
30576                     var years = [];
30577                     _this.fireEvent('years', _this, years);
30578                     return years;
30579                 })(),
30580                 fields : [ 'value' ]
30581             }),
30582             listeners : {
30583                 select : function (_self, record, index)
30584                 {
30585                     _this.setValue(_this.getValue());
30586                 }
30587             }
30588         });
30589
30590         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30591     },
30592     
30593     setValue : function(v, format)
30594     {
30595         this.inputEl.dom.value = v;
30596         
30597         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30598         
30599         var d = Date.parseDate(v, f);
30600         
30601         if(!d){
30602             this.validate();
30603             return;
30604         }
30605         
30606         this.setDay(d.format(this.dayFormat));
30607         this.setMonth(d.format(this.monthFormat));
30608         this.setYear(d.format(this.yearFormat));
30609         
30610         this.validate();
30611         
30612         return;
30613     },
30614     
30615     setDay : function(v)
30616     {
30617         this.dayField.setValue(v);
30618         this.inputEl.dom.value = this.getValue();
30619         this.validate();
30620         return;
30621     },
30622     
30623     setMonth : function(v)
30624     {
30625         this.monthField.setValue(v, true);
30626         this.inputEl.dom.value = this.getValue();
30627         this.validate();
30628         return;
30629     },
30630     
30631     setYear : function(v)
30632     {
30633         this.yearField.setValue(v);
30634         this.inputEl.dom.value = this.getValue();
30635         this.validate();
30636         return;
30637     },
30638     
30639     getDay : function()
30640     {
30641         return this.dayField.getValue();
30642     },
30643     
30644     getMonth : function()
30645     {
30646         return this.monthField.getValue();
30647     },
30648     
30649     getYear : function()
30650     {
30651         return this.yearField.getValue();
30652     },
30653     
30654     getValue : function()
30655     {
30656         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30657         
30658         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30659         
30660         return date;
30661     },
30662     
30663     reset : function()
30664     {
30665         this.setDay('');
30666         this.setMonth('');
30667         this.setYear('');
30668         this.inputEl.dom.value = '';
30669         this.validate();
30670         return;
30671     },
30672     
30673     validate : function()
30674     {
30675         var d = this.dayField.validate();
30676         var m = this.monthField.validate();
30677         var y = this.yearField.validate();
30678         
30679         var valid = true;
30680         
30681         if(
30682                 (!this.dayAllowBlank && !d) ||
30683                 (!this.monthAllowBlank && !m) ||
30684                 (!this.yearAllowBlank && !y)
30685         ){
30686             valid = false;
30687         }
30688         
30689         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30690             return valid;
30691         }
30692         
30693         if(valid){
30694             this.markValid();
30695             return valid;
30696         }
30697         
30698         this.markInvalid();
30699         
30700         return valid;
30701     },
30702     
30703     markValid : function()
30704     {
30705         
30706         var label = this.el.select('label', true).first();
30707         var icon = this.el.select('i.fa-star', true).first();
30708
30709         if(label && icon){
30710             icon.remove();
30711         }
30712         
30713         this.fireEvent('valid', this);
30714     },
30715     
30716      /**
30717      * Mark this field as invalid
30718      * @param {String} msg The validation message
30719      */
30720     markInvalid : function(msg)
30721     {
30722         
30723         var label = this.el.select('label', true).first();
30724         var icon = this.el.select('i.fa-star', true).first();
30725
30726         if(label && !icon){
30727             this.el.select('.roo-date-split-field-label', true).createChild({
30728                 tag : 'i',
30729                 cls : 'text-danger fa fa-lg fa-star',
30730                 tooltip : 'This field is required',
30731                 style : 'margin-right:5px;'
30732             }, label, true);
30733         }
30734         
30735         this.fireEvent('invalid', this, msg);
30736     },
30737     
30738     clearInvalid : function()
30739     {
30740         var label = this.el.select('label', true).first();
30741         var icon = this.el.select('i.fa-star', true).first();
30742
30743         if(label && icon){
30744             icon.remove();
30745         }
30746         
30747         this.fireEvent('valid', this);
30748     },
30749     
30750     getName: function()
30751     {
30752         return this.name;
30753     }
30754     
30755 });
30756
30757  /**
30758  *
30759  * This is based on 
30760  * http://masonry.desandro.com
30761  *
30762  * The idea is to render all the bricks based on vertical width...
30763  *
30764  * The original code extends 'outlayer' - we might need to use that....
30765  * 
30766  */
30767
30768
30769 /**
30770  * @class Roo.bootstrap.LayoutMasonry
30771  * @extends Roo.bootstrap.Component
30772  * Bootstrap Layout Masonry class
30773  * 
30774  * @constructor
30775  * Create a new Element
30776  * @param {Object} config The config object
30777  */
30778
30779 Roo.bootstrap.LayoutMasonry = function(config){
30780     
30781     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30782     
30783     this.bricks = [];
30784     
30785     Roo.bootstrap.LayoutMasonry.register(this);
30786     
30787     this.addEvents({
30788         // raw events
30789         /**
30790          * @event layout
30791          * Fire after layout the items
30792          * @param {Roo.bootstrap.LayoutMasonry} this
30793          * @param {Roo.EventObject} e
30794          */
30795         "layout" : true
30796     });
30797     
30798 };
30799
30800 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30801     
30802     /**
30803      * @cfg {Boolean} isLayoutInstant = no animation?
30804      */   
30805     isLayoutInstant : false, // needed?
30806    
30807     /**
30808      * @cfg {Number} boxWidth  width of the columns
30809      */   
30810     boxWidth : 450,
30811     
30812       /**
30813      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30814      */   
30815     boxHeight : 0,
30816     
30817     /**
30818      * @cfg {Number} padWidth padding below box..
30819      */   
30820     padWidth : 10, 
30821     
30822     /**
30823      * @cfg {Number} gutter gutter width..
30824      */   
30825     gutter : 10,
30826     
30827      /**
30828      * @cfg {Number} maxCols maximum number of columns
30829      */   
30830     
30831     maxCols: 0,
30832     
30833     /**
30834      * @cfg {Boolean} isAutoInitial defalut true
30835      */   
30836     isAutoInitial : true, 
30837     
30838     containerWidth: 0,
30839     
30840     /**
30841      * @cfg {Boolean} isHorizontal defalut false
30842      */   
30843     isHorizontal : false, 
30844
30845     currentSize : null,
30846     
30847     tag: 'div',
30848     
30849     cls: '',
30850     
30851     bricks: null, //CompositeElement
30852     
30853     cols : 1,
30854     
30855     _isLayoutInited : false,
30856     
30857 //    isAlternative : false, // only use for vertical layout...
30858     
30859     /**
30860      * @cfg {Number} alternativePadWidth padding below box..
30861      */   
30862     alternativePadWidth : 50,
30863     
30864     selectedBrick : [],
30865     
30866     getAutoCreate : function(){
30867         
30868         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30869         
30870         var cfg = {
30871             tag: this.tag,
30872             cls: 'blog-masonary-wrapper ' + this.cls,
30873             cn : {
30874                 cls : 'mas-boxes masonary'
30875             }
30876         };
30877         
30878         return cfg;
30879     },
30880     
30881     getChildContainer: function( )
30882     {
30883         if (this.boxesEl) {
30884             return this.boxesEl;
30885         }
30886         
30887         this.boxesEl = this.el.select('.mas-boxes').first();
30888         
30889         return this.boxesEl;
30890     },
30891     
30892     
30893     initEvents : function()
30894     {
30895         var _this = this;
30896         
30897         if(this.isAutoInitial){
30898             Roo.log('hook children rendered');
30899             this.on('childrenrendered', function() {
30900                 Roo.log('children rendered');
30901                 _this.initial();
30902             } ,this);
30903         }
30904     },
30905     
30906     initial : function()
30907     {
30908         this.selectedBrick = [];
30909         
30910         this.currentSize = this.el.getBox(true);
30911         
30912         Roo.EventManager.onWindowResize(this.resize, this); 
30913
30914         if(!this.isAutoInitial){
30915             this.layout();
30916             return;
30917         }
30918         
30919         this.layout();
30920         
30921         return;
30922         //this.layout.defer(500,this);
30923         
30924     },
30925     
30926     resize : function()
30927     {
30928         var cs = this.el.getBox(true);
30929         
30930         if (
30931                 this.currentSize.width == cs.width && 
30932                 this.currentSize.x == cs.x && 
30933                 this.currentSize.height == cs.height && 
30934                 this.currentSize.y == cs.y 
30935         ) {
30936             Roo.log("no change in with or X or Y");
30937             return;
30938         }
30939         
30940         this.currentSize = cs;
30941         
30942         this.layout();
30943         
30944     },
30945     
30946     layout : function()
30947     {   
30948         this._resetLayout();
30949         
30950         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30951         
30952         this.layoutItems( isInstant );
30953       
30954         this._isLayoutInited = true;
30955         
30956         this.fireEvent('layout', this);
30957         
30958     },
30959     
30960     _resetLayout : function()
30961     {
30962         if(this.isHorizontal){
30963             this.horizontalMeasureColumns();
30964             return;
30965         }
30966         
30967         this.verticalMeasureColumns();
30968         
30969     },
30970     
30971     verticalMeasureColumns : function()
30972     {
30973         this.getContainerWidth();
30974         
30975 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30976 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30977 //            return;
30978 //        }
30979         
30980         var boxWidth = this.boxWidth + this.padWidth;
30981         
30982         if(this.containerWidth < this.boxWidth){
30983             boxWidth = this.containerWidth
30984         }
30985         
30986         var containerWidth = this.containerWidth;
30987         
30988         var cols = Math.floor(containerWidth / boxWidth);
30989         
30990         this.cols = Math.max( cols, 1 );
30991         
30992         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30993         
30994         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30995         
30996         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30997         
30998         this.colWidth = boxWidth + avail - this.padWidth;
30999         
31000         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31001         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31002     },
31003     
31004     horizontalMeasureColumns : function()
31005     {
31006         this.getContainerWidth();
31007         
31008         var boxWidth = this.boxWidth;
31009         
31010         if(this.containerWidth < boxWidth){
31011             boxWidth = this.containerWidth;
31012         }
31013         
31014         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31015         
31016         this.el.setHeight(boxWidth);
31017         
31018     },
31019     
31020     getContainerWidth : function()
31021     {
31022         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31023     },
31024     
31025     layoutItems : function( isInstant )
31026     {
31027         Roo.log(this.bricks);
31028         
31029         var items = Roo.apply([], this.bricks);
31030         
31031         if(this.isHorizontal){
31032             this._horizontalLayoutItems( items , isInstant );
31033             return;
31034         }
31035         
31036 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31037 //            this._verticalAlternativeLayoutItems( items , isInstant );
31038 //            return;
31039 //        }
31040         
31041         this._verticalLayoutItems( items , isInstant );
31042         
31043     },
31044     
31045     _verticalLayoutItems : function ( items , isInstant)
31046     {
31047         if ( !items || !items.length ) {
31048             return;
31049         }
31050         
31051         var standard = [
31052             ['xs', 'xs', 'xs', 'tall'],
31053             ['xs', 'xs', 'tall'],
31054             ['xs', 'xs', 'sm'],
31055             ['xs', 'xs', 'xs'],
31056             ['xs', 'tall'],
31057             ['xs', 'sm'],
31058             ['xs', 'xs'],
31059             ['xs'],
31060             
31061             ['sm', 'xs', 'xs'],
31062             ['sm', 'xs'],
31063             ['sm'],
31064             
31065             ['tall', 'xs', 'xs', 'xs'],
31066             ['tall', 'xs', 'xs'],
31067             ['tall', 'xs'],
31068             ['tall']
31069             
31070         ];
31071         
31072         var queue = [];
31073         
31074         var boxes = [];
31075         
31076         var box = [];
31077         
31078         Roo.each(items, function(item, k){
31079             
31080             switch (item.size) {
31081                 // these layouts take up a full box,
31082                 case 'md' :
31083                 case 'md-left' :
31084                 case 'md-right' :
31085                 case 'wide' :
31086                     
31087                     if(box.length){
31088                         boxes.push(box);
31089                         box = [];
31090                     }
31091                     
31092                     boxes.push([item]);
31093                     
31094                     break;
31095                     
31096                 case 'xs' :
31097                 case 'sm' :
31098                 case 'tall' :
31099                     
31100                     box.push(item);
31101                     
31102                     break;
31103                 default :
31104                     break;
31105                     
31106             }
31107             
31108         }, this);
31109         
31110         if(box.length){
31111             boxes.push(box);
31112             box = [];
31113         }
31114         
31115         var filterPattern = function(box, length)
31116         {
31117             if(!box.length){
31118                 return;
31119             }
31120             
31121             var match = false;
31122             
31123             var pattern = box.slice(0, length);
31124             
31125             var format = [];
31126             
31127             Roo.each(pattern, function(i){
31128                 format.push(i.size);
31129             }, this);
31130             
31131             Roo.each(standard, function(s){
31132                 
31133                 if(String(s) != String(format)){
31134                     return;
31135                 }
31136                 
31137                 match = true;
31138                 return false;
31139                 
31140             }, this);
31141             
31142             if(!match && length == 1){
31143                 return;
31144             }
31145             
31146             if(!match){
31147                 filterPattern(box, length - 1);
31148                 return;
31149             }
31150                 
31151             queue.push(pattern);
31152
31153             box = box.slice(length, box.length);
31154
31155             filterPattern(box, 4);
31156
31157             return;
31158             
31159         }
31160         
31161         Roo.each(boxes, function(box, k){
31162             
31163             if(!box.length){
31164                 return;
31165             }
31166             
31167             if(box.length == 1){
31168                 queue.push(box);
31169                 return;
31170             }
31171             
31172             filterPattern(box, 4);
31173             
31174         }, this);
31175         
31176         this._processVerticalLayoutQueue( queue, isInstant );
31177         
31178     },
31179     
31180 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31181 //    {
31182 //        if ( !items || !items.length ) {
31183 //            return;
31184 //        }
31185 //
31186 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31187 //        
31188 //    },
31189     
31190     _horizontalLayoutItems : function ( items , isInstant)
31191     {
31192         if ( !items || !items.length || items.length < 3) {
31193             return;
31194         }
31195         
31196         items.reverse();
31197         
31198         var eItems = items.slice(0, 3);
31199         
31200         items = items.slice(3, items.length);
31201         
31202         var standard = [
31203             ['xs', 'xs', 'xs', 'wide'],
31204             ['xs', 'xs', 'wide'],
31205             ['xs', 'xs', 'sm'],
31206             ['xs', 'xs', 'xs'],
31207             ['xs', 'wide'],
31208             ['xs', 'sm'],
31209             ['xs', 'xs'],
31210             ['xs'],
31211             
31212             ['sm', 'xs', 'xs'],
31213             ['sm', 'xs'],
31214             ['sm'],
31215             
31216             ['wide', 'xs', 'xs', 'xs'],
31217             ['wide', 'xs', 'xs'],
31218             ['wide', 'xs'],
31219             ['wide'],
31220             
31221             ['wide-thin']
31222         ];
31223         
31224         var queue = [];
31225         
31226         var boxes = [];
31227         
31228         var box = [];
31229         
31230         Roo.each(items, function(item, k){
31231             
31232             switch (item.size) {
31233                 case 'md' :
31234                 case 'md-left' :
31235                 case 'md-right' :
31236                 case 'tall' :
31237                     
31238                     if(box.length){
31239                         boxes.push(box);
31240                         box = [];
31241                     }
31242                     
31243                     boxes.push([item]);
31244                     
31245                     break;
31246                     
31247                 case 'xs' :
31248                 case 'sm' :
31249                 case 'wide' :
31250                 case 'wide-thin' :
31251                     
31252                     box.push(item);
31253                     
31254                     break;
31255                 default :
31256                     break;
31257                     
31258             }
31259             
31260         }, this);
31261         
31262         if(box.length){
31263             boxes.push(box);
31264             box = [];
31265         }
31266         
31267         var filterPattern = function(box, length)
31268         {
31269             if(!box.length){
31270                 return;
31271             }
31272             
31273             var match = false;
31274             
31275             var pattern = box.slice(0, length);
31276             
31277             var format = [];
31278             
31279             Roo.each(pattern, function(i){
31280                 format.push(i.size);
31281             }, this);
31282             
31283             Roo.each(standard, function(s){
31284                 
31285                 if(String(s) != String(format)){
31286                     return;
31287                 }
31288                 
31289                 match = true;
31290                 return false;
31291                 
31292             }, this);
31293             
31294             if(!match && length == 1){
31295                 return;
31296             }
31297             
31298             if(!match){
31299                 filterPattern(box, length - 1);
31300                 return;
31301             }
31302                 
31303             queue.push(pattern);
31304
31305             box = box.slice(length, box.length);
31306
31307             filterPattern(box, 4);
31308
31309             return;
31310             
31311         }
31312         
31313         Roo.each(boxes, function(box, k){
31314             
31315             if(!box.length){
31316                 return;
31317             }
31318             
31319             if(box.length == 1){
31320                 queue.push(box);
31321                 return;
31322             }
31323             
31324             filterPattern(box, 4);
31325             
31326         }, this);
31327         
31328         
31329         var prune = [];
31330         
31331         var pos = this.el.getBox(true);
31332         
31333         var minX = pos.x;
31334         
31335         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31336         
31337         var hit_end = false;
31338         
31339         Roo.each(queue, function(box){
31340             
31341             if(hit_end){
31342                 
31343                 Roo.each(box, function(b){
31344                 
31345                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31346                     b.el.hide();
31347
31348                 }, this);
31349
31350                 return;
31351             }
31352             
31353             var mx = 0;
31354             
31355             Roo.each(box, function(b){
31356                 
31357                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31358                 b.el.show();
31359
31360                 mx = Math.max(mx, b.x);
31361                 
31362             }, this);
31363             
31364             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31365             
31366             if(maxX < minX){
31367                 
31368                 Roo.each(box, function(b){
31369                 
31370                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31371                     b.el.hide();
31372                     
31373                 }, this);
31374                 
31375                 hit_end = true;
31376                 
31377                 return;
31378             }
31379             
31380             prune.push(box);
31381             
31382         }, this);
31383         
31384         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31385     },
31386     
31387     /** Sets position of item in DOM
31388     * @param {Element} item
31389     * @param {Number} x - horizontal position
31390     * @param {Number} y - vertical position
31391     * @param {Boolean} isInstant - disables transitions
31392     */
31393     _processVerticalLayoutQueue : function( queue, isInstant )
31394     {
31395         var pos = this.el.getBox(true);
31396         var x = pos.x;
31397         var y = pos.y;
31398         var maxY = [];
31399         
31400         for (var i = 0; i < this.cols; i++){
31401             maxY[i] = pos.y;
31402         }
31403         
31404         Roo.each(queue, function(box, k){
31405             
31406             var col = k % this.cols;
31407             
31408             Roo.each(box, function(b,kk){
31409                 
31410                 b.el.position('absolute');
31411                 
31412                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31413                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31414                 
31415                 if(b.size == 'md-left' || b.size == 'md-right'){
31416                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31417                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31418                 }
31419                 
31420                 b.el.setWidth(width);
31421                 b.el.setHeight(height);
31422                 // iframe?
31423                 b.el.select('iframe',true).setSize(width,height);
31424                 
31425             }, this);
31426             
31427             for (var i = 0; i < this.cols; i++){
31428                 
31429                 if(maxY[i] < maxY[col]){
31430                     col = i;
31431                     continue;
31432                 }
31433                 
31434                 col = Math.min(col, i);
31435                 
31436             }
31437             
31438             x = pos.x + col * (this.colWidth + this.padWidth);
31439             
31440             y = maxY[col];
31441             
31442             var positions = [];
31443             
31444             switch (box.length){
31445                 case 1 :
31446                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31447                     break;
31448                 case 2 :
31449                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31450                     break;
31451                 case 3 :
31452                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31453                     break;
31454                 case 4 :
31455                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31456                     break;
31457                 default :
31458                     break;
31459             }
31460             
31461             Roo.each(box, function(b,kk){
31462                 
31463                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31464                 
31465                 var sz = b.el.getSize();
31466                 
31467                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31468                 
31469             }, this);
31470             
31471         }, this);
31472         
31473         var mY = 0;
31474         
31475         for (var i = 0; i < this.cols; i++){
31476             mY = Math.max(mY, maxY[i]);
31477         }
31478         
31479         this.el.setHeight(mY - pos.y);
31480         
31481     },
31482     
31483 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31484 //    {
31485 //        var pos = this.el.getBox(true);
31486 //        var x = pos.x;
31487 //        var y = pos.y;
31488 //        var maxX = pos.right;
31489 //        
31490 //        var maxHeight = 0;
31491 //        
31492 //        Roo.each(items, function(item, k){
31493 //            
31494 //            var c = k % 2;
31495 //            
31496 //            item.el.position('absolute');
31497 //                
31498 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31499 //
31500 //            item.el.setWidth(width);
31501 //
31502 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31503 //
31504 //            item.el.setHeight(height);
31505 //            
31506 //            if(c == 0){
31507 //                item.el.setXY([x, y], isInstant ? false : true);
31508 //            } else {
31509 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31510 //            }
31511 //            
31512 //            y = y + height + this.alternativePadWidth;
31513 //            
31514 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31515 //            
31516 //        }, this);
31517 //        
31518 //        this.el.setHeight(maxHeight);
31519 //        
31520 //    },
31521     
31522     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31523     {
31524         var pos = this.el.getBox(true);
31525         
31526         var minX = pos.x;
31527         var minY = pos.y;
31528         
31529         var maxX = pos.right;
31530         
31531         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31532         
31533         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31534         
31535         Roo.each(queue, function(box, k){
31536             
31537             Roo.each(box, function(b, kk){
31538                 
31539                 b.el.position('absolute');
31540                 
31541                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31542                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31543                 
31544                 if(b.size == 'md-left' || b.size == 'md-right'){
31545                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31546                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31547                 }
31548                 
31549                 b.el.setWidth(width);
31550                 b.el.setHeight(height);
31551                 
31552             }, this);
31553             
31554             if(!box.length){
31555                 return;
31556             }
31557             
31558             var positions = [];
31559             
31560             switch (box.length){
31561                 case 1 :
31562                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31563                     break;
31564                 case 2 :
31565                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31566                     break;
31567                 case 3 :
31568                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31569                     break;
31570                 case 4 :
31571                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31572                     break;
31573                 default :
31574                     break;
31575             }
31576             
31577             Roo.each(box, function(b,kk){
31578                 
31579                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31580                 
31581                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31582                 
31583             }, this);
31584             
31585         }, this);
31586         
31587     },
31588     
31589     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31590     {
31591         Roo.each(eItems, function(b,k){
31592             
31593             b.size = (k == 0) ? 'sm' : 'xs';
31594             b.x = (k == 0) ? 2 : 1;
31595             b.y = (k == 0) ? 2 : 1;
31596             
31597             b.el.position('absolute');
31598             
31599             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31600                 
31601             b.el.setWidth(width);
31602             
31603             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31604             
31605             b.el.setHeight(height);
31606             
31607         }, this);
31608
31609         var positions = [];
31610         
31611         positions.push({
31612             x : maxX - this.unitWidth * 2 - this.gutter,
31613             y : minY
31614         });
31615         
31616         positions.push({
31617             x : maxX - this.unitWidth,
31618             y : minY + (this.unitWidth + this.gutter) * 2
31619         });
31620         
31621         positions.push({
31622             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31623             y : minY
31624         });
31625         
31626         Roo.each(eItems, function(b,k){
31627             
31628             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31629
31630         }, this);
31631         
31632     },
31633     
31634     getVerticalOneBoxColPositions : function(x, y, box)
31635     {
31636         var pos = [];
31637         
31638         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31639         
31640         if(box[0].size == 'md-left'){
31641             rand = 0;
31642         }
31643         
31644         if(box[0].size == 'md-right'){
31645             rand = 1;
31646         }
31647         
31648         pos.push({
31649             x : x + (this.unitWidth + this.gutter) * rand,
31650             y : y
31651         });
31652         
31653         return pos;
31654     },
31655     
31656     getVerticalTwoBoxColPositions : function(x, y, box)
31657     {
31658         var pos = [];
31659         
31660         if(box[0].size == 'xs'){
31661             
31662             pos.push({
31663                 x : x,
31664                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31665             });
31666
31667             pos.push({
31668                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31669                 y : y
31670             });
31671             
31672             return pos;
31673             
31674         }
31675         
31676         pos.push({
31677             x : x,
31678             y : y
31679         });
31680
31681         pos.push({
31682             x : x + (this.unitWidth + this.gutter) * 2,
31683             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31684         });
31685         
31686         return pos;
31687         
31688     },
31689     
31690     getVerticalThreeBoxColPositions : function(x, y, box)
31691     {
31692         var pos = [];
31693         
31694         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31695             
31696             pos.push({
31697                 x : x,
31698                 y : y
31699             });
31700
31701             pos.push({
31702                 x : x + (this.unitWidth + this.gutter) * 1,
31703                 y : y
31704             });
31705             
31706             pos.push({
31707                 x : x + (this.unitWidth + this.gutter) * 2,
31708                 y : y
31709             });
31710             
31711             return pos;
31712             
31713         }
31714         
31715         if(box[0].size == 'xs' && box[1].size == 'xs'){
31716             
31717             pos.push({
31718                 x : x,
31719                 y : y
31720             });
31721
31722             pos.push({
31723                 x : x,
31724                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31725             });
31726             
31727             pos.push({
31728                 x : x + (this.unitWidth + this.gutter) * 1,
31729                 y : y
31730             });
31731             
31732             return pos;
31733             
31734         }
31735         
31736         pos.push({
31737             x : x,
31738             y : y
31739         });
31740
31741         pos.push({
31742             x : x + (this.unitWidth + this.gutter) * 2,
31743             y : y
31744         });
31745
31746         pos.push({
31747             x : x + (this.unitWidth + this.gutter) * 2,
31748             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31749         });
31750             
31751         return pos;
31752         
31753     },
31754     
31755     getVerticalFourBoxColPositions : function(x, y, box)
31756     {
31757         var pos = [];
31758         
31759         if(box[0].size == 'xs'){
31760             
31761             pos.push({
31762                 x : x,
31763                 y : y
31764             });
31765
31766             pos.push({
31767                 x : x,
31768                 y : y + (this.unitHeight + this.gutter) * 1
31769             });
31770             
31771             pos.push({
31772                 x : x,
31773                 y : y + (this.unitHeight + this.gutter) * 2
31774             });
31775             
31776             pos.push({
31777                 x : x + (this.unitWidth + this.gutter) * 1,
31778                 y : y
31779             });
31780             
31781             return pos;
31782             
31783         }
31784         
31785         pos.push({
31786             x : x,
31787             y : y
31788         });
31789
31790         pos.push({
31791             x : x + (this.unitWidth + this.gutter) * 2,
31792             y : y
31793         });
31794
31795         pos.push({
31796             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31797             y : y + (this.unitHeight + this.gutter) * 1
31798         });
31799
31800         pos.push({
31801             x : x + (this.unitWidth + this.gutter) * 2,
31802             y : y + (this.unitWidth + this.gutter) * 2
31803         });
31804
31805         return pos;
31806         
31807     },
31808     
31809     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31810     {
31811         var pos = [];
31812         
31813         if(box[0].size == 'md-left'){
31814             pos.push({
31815                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31816                 y : minY
31817             });
31818             
31819             return pos;
31820         }
31821         
31822         if(box[0].size == 'md-right'){
31823             pos.push({
31824                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31825                 y : minY + (this.unitWidth + this.gutter) * 1
31826             });
31827             
31828             return pos;
31829         }
31830         
31831         var rand = Math.floor(Math.random() * (4 - box[0].y));
31832         
31833         pos.push({
31834             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31835             y : minY + (this.unitWidth + this.gutter) * rand
31836         });
31837         
31838         return pos;
31839         
31840     },
31841     
31842     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31843     {
31844         var pos = [];
31845         
31846         if(box[0].size == 'xs'){
31847             
31848             pos.push({
31849                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31850                 y : minY
31851             });
31852
31853             pos.push({
31854                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31855                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31856             });
31857             
31858             return pos;
31859             
31860         }
31861         
31862         pos.push({
31863             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31864             y : minY
31865         });
31866
31867         pos.push({
31868             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31869             y : minY + (this.unitWidth + this.gutter) * 2
31870         });
31871         
31872         return pos;
31873         
31874     },
31875     
31876     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31877     {
31878         var pos = [];
31879         
31880         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31881             
31882             pos.push({
31883                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31884                 y : minY
31885             });
31886
31887             pos.push({
31888                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31889                 y : minY + (this.unitWidth + this.gutter) * 1
31890             });
31891             
31892             pos.push({
31893                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31894                 y : minY + (this.unitWidth + this.gutter) * 2
31895             });
31896             
31897             return pos;
31898             
31899         }
31900         
31901         if(box[0].size == 'xs' && box[1].size == 'xs'){
31902             
31903             pos.push({
31904                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31905                 y : minY
31906             });
31907
31908             pos.push({
31909                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31910                 y : minY
31911             });
31912             
31913             pos.push({
31914                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31915                 y : minY + (this.unitWidth + this.gutter) * 1
31916             });
31917             
31918             return pos;
31919             
31920         }
31921         
31922         pos.push({
31923             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31924             y : minY
31925         });
31926
31927         pos.push({
31928             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31929             y : minY + (this.unitWidth + this.gutter) * 2
31930         });
31931
31932         pos.push({
31933             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31934             y : minY + (this.unitWidth + this.gutter) * 2
31935         });
31936             
31937         return pos;
31938         
31939     },
31940     
31941     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31942     {
31943         var pos = [];
31944         
31945         if(box[0].size == 'xs'){
31946             
31947             pos.push({
31948                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31949                 y : minY
31950             });
31951
31952             pos.push({
31953                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31954                 y : minY
31955             });
31956             
31957             pos.push({
31958                 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),
31959                 y : minY
31960             });
31961             
31962             pos.push({
31963                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31964                 y : minY + (this.unitWidth + this.gutter) * 1
31965             });
31966             
31967             return pos;
31968             
31969         }
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[1].x - this.gutter * (box[1].x - 1),
31978             y : minY + (this.unitWidth + this.gutter) * 2
31979         });
31980         
31981         pos.push({
31982             x : maxX - 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 + (this.unitWidth + this.gutter) * 2
31984         });
31985         
31986         pos.push({
31987             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),
31988             y : minY + (this.unitWidth + this.gutter) * 2
31989         });
31990
31991         return pos;
31992         
31993     },
31994     
31995     /**
31996     * remove a Masonry Brick
31997     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31998     */
31999     removeBrick : function(brick_id)
32000     {
32001         if (!brick_id) {
32002             return;
32003         }
32004         
32005         for (var i = 0; i<this.bricks.length; i++) {
32006             if (this.bricks[i].id == brick_id) {
32007                 this.bricks.splice(i,1);
32008                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32009                 this.initial();
32010             }
32011         }
32012     },
32013     
32014     /**
32015     * adds a Masonry Brick
32016     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32017     */
32018     addBrick : function(cfg)
32019     {
32020         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32021         //this.register(cn);
32022         cn.parentId = this.id;
32023         cn.render(this.el);
32024         return cn;
32025     },
32026     
32027     /**
32028     * register a Masonry Brick
32029     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32030     */
32031     
32032     register : function(brick)
32033     {
32034         this.bricks.push(brick);
32035         brick.masonryId = this.id;
32036     },
32037     
32038     /**
32039     * clear all the Masonry Brick
32040     */
32041     clearAll : function()
32042     {
32043         this.bricks = [];
32044         //this.getChildContainer().dom.innerHTML = "";
32045         this.el.dom.innerHTML = '';
32046     },
32047     
32048     getSelected : function()
32049     {
32050         if (!this.selectedBrick) {
32051             return false;
32052         }
32053         
32054         return this.selectedBrick;
32055     }
32056 });
32057
32058 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32059     
32060     groups: {},
32061      /**
32062     * register a Masonry Layout
32063     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32064     */
32065     
32066     register : function(layout)
32067     {
32068         this.groups[layout.id] = layout;
32069     },
32070     /**
32071     * fetch a  Masonry Layout based on the masonry layout ID
32072     * @param {string} the masonry layout to add
32073     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32074     */
32075     
32076     get: function(layout_id) {
32077         if (typeof(this.groups[layout_id]) == 'undefined') {
32078             return false;
32079         }
32080         return this.groups[layout_id] ;
32081     }
32082     
32083     
32084     
32085 });
32086
32087  
32088
32089  /**
32090  *
32091  * This is based on 
32092  * http://masonry.desandro.com
32093  *
32094  * The idea is to render all the bricks based on vertical width...
32095  *
32096  * The original code extends 'outlayer' - we might need to use that....
32097  * 
32098  */
32099
32100
32101 /**
32102  * @class Roo.bootstrap.LayoutMasonryAuto
32103  * @extends Roo.bootstrap.Component
32104  * Bootstrap Layout Masonry class
32105  * 
32106  * @constructor
32107  * Create a new Element
32108  * @param {Object} config The config object
32109  */
32110
32111 Roo.bootstrap.LayoutMasonryAuto = function(config){
32112     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32113 };
32114
32115 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32116     
32117       /**
32118      * @cfg {Boolean} isFitWidth  - resize the width..
32119      */   
32120     isFitWidth : false,  // options..
32121     /**
32122      * @cfg {Boolean} isOriginLeft = left align?
32123      */   
32124     isOriginLeft : true,
32125     /**
32126      * @cfg {Boolean} isOriginTop = top align?
32127      */   
32128     isOriginTop : false,
32129     /**
32130      * @cfg {Boolean} isLayoutInstant = no animation?
32131      */   
32132     isLayoutInstant : false, // needed?
32133     /**
32134      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32135      */   
32136     isResizingContainer : true,
32137     /**
32138      * @cfg {Number} columnWidth  width of the columns 
32139      */   
32140     
32141     columnWidth : 0,
32142     
32143     /**
32144      * @cfg {Number} maxCols maximum number of columns
32145      */   
32146     
32147     maxCols: 0,
32148     /**
32149      * @cfg {Number} padHeight padding below box..
32150      */   
32151     
32152     padHeight : 10, 
32153     
32154     /**
32155      * @cfg {Boolean} isAutoInitial defalut true
32156      */   
32157     
32158     isAutoInitial : true, 
32159     
32160     // private?
32161     gutter : 0,
32162     
32163     containerWidth: 0,
32164     initialColumnWidth : 0,
32165     currentSize : null,
32166     
32167     colYs : null, // array.
32168     maxY : 0,
32169     padWidth: 10,
32170     
32171     
32172     tag: 'div',
32173     cls: '',
32174     bricks: null, //CompositeElement
32175     cols : 0, // array?
32176     // element : null, // wrapped now this.el
32177     _isLayoutInited : null, 
32178     
32179     
32180     getAutoCreate : function(){
32181         
32182         var cfg = {
32183             tag: this.tag,
32184             cls: 'blog-masonary-wrapper ' + this.cls,
32185             cn : {
32186                 cls : 'mas-boxes masonary'
32187             }
32188         };
32189         
32190         return cfg;
32191     },
32192     
32193     getChildContainer: function( )
32194     {
32195         if (this.boxesEl) {
32196             return this.boxesEl;
32197         }
32198         
32199         this.boxesEl = this.el.select('.mas-boxes').first();
32200         
32201         return this.boxesEl;
32202     },
32203     
32204     
32205     initEvents : function()
32206     {
32207         var _this = this;
32208         
32209         if(this.isAutoInitial){
32210             Roo.log('hook children rendered');
32211             this.on('childrenrendered', function() {
32212                 Roo.log('children rendered');
32213                 _this.initial();
32214             } ,this);
32215         }
32216         
32217     },
32218     
32219     initial : function()
32220     {
32221         this.reloadItems();
32222
32223         this.currentSize = this.el.getBox(true);
32224
32225         /// was window resize... - let's see if this works..
32226         Roo.EventManager.onWindowResize(this.resize, this); 
32227
32228         if(!this.isAutoInitial){
32229             this.layout();
32230             return;
32231         }
32232         
32233         this.layout.defer(500,this);
32234     },
32235     
32236     reloadItems: function()
32237     {
32238         this.bricks = this.el.select('.masonry-brick', true);
32239         
32240         this.bricks.each(function(b) {
32241             //Roo.log(b.getSize());
32242             if (!b.attr('originalwidth')) {
32243                 b.attr('originalwidth',  b.getSize().width);
32244             }
32245             
32246         });
32247         
32248         Roo.log(this.bricks.elements.length);
32249     },
32250     
32251     resize : function()
32252     {
32253         Roo.log('resize');
32254         var cs = this.el.getBox(true);
32255         
32256         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32257             Roo.log("no change in with or X");
32258             return;
32259         }
32260         this.currentSize = cs;
32261         this.layout();
32262     },
32263     
32264     layout : function()
32265     {
32266          Roo.log('layout');
32267         this._resetLayout();
32268         //this._manageStamps();
32269       
32270         // don't animate first layout
32271         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32272         this.layoutItems( isInstant );
32273       
32274         // flag for initalized
32275         this._isLayoutInited = true;
32276     },
32277     
32278     layoutItems : function( isInstant )
32279     {
32280         //var items = this._getItemsForLayout( this.items );
32281         // original code supports filtering layout items.. we just ignore it..
32282         
32283         this._layoutItems( this.bricks , isInstant );
32284       
32285         this._postLayout();
32286     },
32287     _layoutItems : function ( items , isInstant)
32288     {
32289        //this.fireEvent( 'layout', this, items );
32290     
32291
32292         if ( !items || !items.elements.length ) {
32293           // no items, emit event with empty array
32294             return;
32295         }
32296
32297         var queue = [];
32298         items.each(function(item) {
32299             Roo.log("layout item");
32300             Roo.log(item);
32301             // get x/y object from method
32302             var position = this._getItemLayoutPosition( item );
32303             // enqueue
32304             position.item = item;
32305             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32306             queue.push( position );
32307         }, this);
32308       
32309         this._processLayoutQueue( queue );
32310     },
32311     /** Sets position of item in DOM
32312     * @param {Element} item
32313     * @param {Number} x - horizontal position
32314     * @param {Number} y - vertical position
32315     * @param {Boolean} isInstant - disables transitions
32316     */
32317     _processLayoutQueue : function( queue )
32318     {
32319         for ( var i=0, len = queue.length; i < len; i++ ) {
32320             var obj = queue[i];
32321             obj.item.position('absolute');
32322             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32323         }
32324     },
32325       
32326     
32327     /**
32328     * Any logic you want to do after each layout,
32329     * i.e. size the container
32330     */
32331     _postLayout : function()
32332     {
32333         this.resizeContainer();
32334     },
32335     
32336     resizeContainer : function()
32337     {
32338         if ( !this.isResizingContainer ) {
32339             return;
32340         }
32341         var size = this._getContainerSize();
32342         if ( size ) {
32343             this.el.setSize(size.width,size.height);
32344             this.boxesEl.setSize(size.width,size.height);
32345         }
32346     },
32347     
32348     
32349     
32350     _resetLayout : function()
32351     {
32352         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32353         this.colWidth = this.el.getWidth();
32354         //this.gutter = this.el.getWidth(); 
32355         
32356         this.measureColumns();
32357
32358         // reset column Y
32359         var i = this.cols;
32360         this.colYs = [];
32361         while (i--) {
32362             this.colYs.push( 0 );
32363         }
32364     
32365         this.maxY = 0;
32366     },
32367
32368     measureColumns : function()
32369     {
32370         this.getContainerWidth();
32371       // if columnWidth is 0, default to outerWidth of first item
32372         if ( !this.columnWidth ) {
32373             var firstItem = this.bricks.first();
32374             Roo.log(firstItem);
32375             this.columnWidth  = this.containerWidth;
32376             if (firstItem && firstItem.attr('originalwidth') ) {
32377                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32378             }
32379             // columnWidth fall back to item of first element
32380             Roo.log("set column width?");
32381                         this.initialColumnWidth = this.columnWidth  ;
32382
32383             // if first elem has no width, default to size of container
32384             
32385         }
32386         
32387         
32388         if (this.initialColumnWidth) {
32389             this.columnWidth = this.initialColumnWidth;
32390         }
32391         
32392         
32393             
32394         // column width is fixed at the top - however if container width get's smaller we should
32395         // reduce it...
32396         
32397         // this bit calcs how man columns..
32398             
32399         var columnWidth = this.columnWidth += this.gutter;
32400       
32401         // calculate columns
32402         var containerWidth = this.containerWidth + this.gutter;
32403         
32404         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32405         // fix rounding errors, typically with gutters
32406         var excess = columnWidth - containerWidth % columnWidth;
32407         
32408         
32409         // if overshoot is less than a pixel, round up, otherwise floor it
32410         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32411         cols = Math[ mathMethod ]( cols );
32412         this.cols = Math.max( cols, 1 );
32413         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32414         
32415          // padding positioning..
32416         var totalColWidth = this.cols * this.columnWidth;
32417         var padavail = this.containerWidth - totalColWidth;
32418         // so for 2 columns - we need 3 'pads'
32419         
32420         var padNeeded = (1+this.cols) * this.padWidth;
32421         
32422         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32423         
32424         this.columnWidth += padExtra
32425         //this.padWidth = Math.floor(padavail /  ( this.cols));
32426         
32427         // adjust colum width so that padding is fixed??
32428         
32429         // we have 3 columns ... total = width * 3
32430         // we have X left over... that should be used by 
32431         
32432         //if (this.expandC) {
32433             
32434         //}
32435         
32436         
32437         
32438     },
32439     
32440     getContainerWidth : function()
32441     {
32442        /* // container is parent if fit width
32443         var container = this.isFitWidth ? this.element.parentNode : this.element;
32444         // check that this.size and size are there
32445         // IE8 triggers resize on body size change, so they might not be
32446         
32447         var size = getSize( container );  //FIXME
32448         this.containerWidth = size && size.innerWidth; //FIXME
32449         */
32450          
32451         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32452         
32453     },
32454     
32455     _getItemLayoutPosition : function( item )  // what is item?
32456     {
32457         // we resize the item to our columnWidth..
32458       
32459         item.setWidth(this.columnWidth);
32460         item.autoBoxAdjust  = false;
32461         
32462         var sz = item.getSize();
32463  
32464         // how many columns does this brick span
32465         var remainder = this.containerWidth % this.columnWidth;
32466         
32467         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32468         // round if off by 1 pixel, otherwise use ceil
32469         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32470         colSpan = Math.min( colSpan, this.cols );
32471         
32472         // normally this should be '1' as we dont' currently allow multi width columns..
32473         
32474         var colGroup = this._getColGroup( colSpan );
32475         // get the minimum Y value from the columns
32476         var minimumY = Math.min.apply( Math, colGroup );
32477         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32478         
32479         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32480          
32481         // position the brick
32482         var position = {
32483             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32484             y: this.currentSize.y + minimumY + this.padHeight
32485         };
32486         
32487         Roo.log(position);
32488         // apply setHeight to necessary columns
32489         var setHeight = minimumY + sz.height + this.padHeight;
32490         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32491         
32492         var setSpan = this.cols + 1 - colGroup.length;
32493         for ( var i = 0; i < setSpan; i++ ) {
32494           this.colYs[ shortColIndex + i ] = setHeight ;
32495         }
32496       
32497         return position;
32498     },
32499     
32500     /**
32501      * @param {Number} colSpan - number of columns the element spans
32502      * @returns {Array} colGroup
32503      */
32504     _getColGroup : function( colSpan )
32505     {
32506         if ( colSpan < 2 ) {
32507           // if brick spans only one column, use all the column Ys
32508           return this.colYs;
32509         }
32510       
32511         var colGroup = [];
32512         // how many different places could this brick fit horizontally
32513         var groupCount = this.cols + 1 - colSpan;
32514         // for each group potential horizontal position
32515         for ( var i = 0; i < groupCount; i++ ) {
32516           // make an array of colY values for that one group
32517           var groupColYs = this.colYs.slice( i, i + colSpan );
32518           // and get the max value of the array
32519           colGroup[i] = Math.max.apply( Math, groupColYs );
32520         }
32521         return colGroup;
32522     },
32523     /*
32524     _manageStamp : function( stamp )
32525     {
32526         var stampSize =  stamp.getSize();
32527         var offset = stamp.getBox();
32528         // get the columns that this stamp affects
32529         var firstX = this.isOriginLeft ? offset.x : offset.right;
32530         var lastX = firstX + stampSize.width;
32531         var firstCol = Math.floor( firstX / this.columnWidth );
32532         firstCol = Math.max( 0, firstCol );
32533         
32534         var lastCol = Math.floor( lastX / this.columnWidth );
32535         // lastCol should not go over if multiple of columnWidth #425
32536         lastCol -= lastX % this.columnWidth ? 0 : 1;
32537         lastCol = Math.min( this.cols - 1, lastCol );
32538         
32539         // set colYs to bottom of the stamp
32540         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32541             stampSize.height;
32542             
32543         for ( var i = firstCol; i <= lastCol; i++ ) {
32544           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32545         }
32546     },
32547     */
32548     
32549     _getContainerSize : function()
32550     {
32551         this.maxY = Math.max.apply( Math, this.colYs );
32552         var size = {
32553             height: this.maxY
32554         };
32555       
32556         if ( this.isFitWidth ) {
32557             size.width = this._getContainerFitWidth();
32558         }
32559       
32560         return size;
32561     },
32562     
32563     _getContainerFitWidth : function()
32564     {
32565         var unusedCols = 0;
32566         // count unused columns
32567         var i = this.cols;
32568         while ( --i ) {
32569           if ( this.colYs[i] !== 0 ) {
32570             break;
32571           }
32572           unusedCols++;
32573         }
32574         // fit container to columns that have been used
32575         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32576     },
32577     
32578     needsResizeLayout : function()
32579     {
32580         var previousWidth = this.containerWidth;
32581         this.getContainerWidth();
32582         return previousWidth !== this.containerWidth;
32583     }
32584  
32585 });
32586
32587  
32588
32589  /*
32590  * - LGPL
32591  *
32592  * element
32593  * 
32594  */
32595
32596 /**
32597  * @class Roo.bootstrap.MasonryBrick
32598  * @extends Roo.bootstrap.Component
32599  * Bootstrap MasonryBrick class
32600  * 
32601  * @constructor
32602  * Create a new MasonryBrick
32603  * @param {Object} config The config object
32604  */
32605
32606 Roo.bootstrap.MasonryBrick = function(config){
32607     
32608     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32609     
32610     Roo.bootstrap.MasonryBrick.register(this);
32611     
32612     this.addEvents({
32613         // raw events
32614         /**
32615          * @event click
32616          * When a MasonryBrick is clcik
32617          * @param {Roo.bootstrap.MasonryBrick} this
32618          * @param {Roo.EventObject} e
32619          */
32620         "click" : true
32621     });
32622 };
32623
32624 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32625     
32626     /**
32627      * @cfg {String} title
32628      */   
32629     title : '',
32630     /**
32631      * @cfg {String} html
32632      */   
32633     html : '',
32634     /**
32635      * @cfg {String} bgimage
32636      */   
32637     bgimage : '',
32638     /**
32639      * @cfg {String} videourl
32640      */   
32641     videourl : '',
32642     /**
32643      * @cfg {String} cls
32644      */   
32645     cls : '',
32646     /**
32647      * @cfg {String} href
32648      */   
32649     href : '',
32650     /**
32651      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32652      */   
32653     size : 'xs',
32654     
32655     /**
32656      * @cfg {String} placetitle (center|bottom)
32657      */   
32658     placetitle : '',
32659     
32660     /**
32661      * @cfg {Boolean} isFitContainer defalut true
32662      */   
32663     isFitContainer : true, 
32664     
32665     /**
32666      * @cfg {Boolean} preventDefault defalut false
32667      */   
32668     preventDefault : false, 
32669     
32670     /**
32671      * @cfg {Boolean} inverse defalut false
32672      */   
32673     maskInverse : false, 
32674     
32675     getAutoCreate : function()
32676     {
32677         if(!this.isFitContainer){
32678             return this.getSplitAutoCreate();
32679         }
32680         
32681         var cls = 'masonry-brick masonry-brick-full';
32682         
32683         if(this.href.length){
32684             cls += ' masonry-brick-link';
32685         }
32686         
32687         if(this.bgimage.length){
32688             cls += ' masonry-brick-image';
32689         }
32690         
32691         if(this.maskInverse){
32692             cls += ' mask-inverse';
32693         }
32694         
32695         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32696             cls += ' enable-mask';
32697         }
32698         
32699         if(this.size){
32700             cls += ' masonry-' + this.size + '-brick';
32701         }
32702         
32703         if(this.placetitle.length){
32704             
32705             switch (this.placetitle) {
32706                 case 'center' :
32707                     cls += ' masonry-center-title';
32708                     break;
32709                 case 'bottom' :
32710                     cls += ' masonry-bottom-title';
32711                     break;
32712                 default:
32713                     break;
32714             }
32715             
32716         } else {
32717             if(!this.html.length && !this.bgimage.length){
32718                 cls += ' masonry-center-title';
32719             }
32720
32721             if(!this.html.length && this.bgimage.length){
32722                 cls += ' masonry-bottom-title';
32723             }
32724         }
32725         
32726         if(this.cls){
32727             cls += ' ' + this.cls;
32728         }
32729         
32730         var cfg = {
32731             tag: (this.href.length) ? 'a' : 'div',
32732             cls: cls,
32733             cn: [
32734                 {
32735                     tag: 'div',
32736                     cls: 'masonry-brick-mask'
32737                 },
32738                 {
32739                     tag: 'div',
32740                     cls: 'masonry-brick-paragraph',
32741                     cn: []
32742                 }
32743             ]
32744         };
32745         
32746         if(this.href.length){
32747             cfg.href = this.href;
32748         }
32749         
32750         var cn = cfg.cn[1].cn;
32751         
32752         if(this.title.length){
32753             cn.push({
32754                 tag: 'h4',
32755                 cls: 'masonry-brick-title',
32756                 html: this.title
32757             });
32758         }
32759         
32760         if(this.html.length){
32761             cn.push({
32762                 tag: 'p',
32763                 cls: 'masonry-brick-text',
32764                 html: this.html
32765             });
32766         }
32767         
32768         if (!this.title.length && !this.html.length) {
32769             cfg.cn[1].cls += ' hide';
32770         }
32771         
32772         if(this.bgimage.length){
32773             cfg.cn.push({
32774                 tag: 'img',
32775                 cls: 'masonry-brick-image-view',
32776                 src: this.bgimage
32777             });
32778         }
32779         
32780         if(this.videourl.length){
32781             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32782             // youtube support only?
32783             cfg.cn.push({
32784                 tag: 'iframe',
32785                 cls: 'masonry-brick-image-view',
32786                 src: vurl,
32787                 frameborder : 0,
32788                 allowfullscreen : true
32789             });
32790         }
32791         
32792         return cfg;
32793         
32794     },
32795     
32796     getSplitAutoCreate : function()
32797     {
32798         var cls = 'masonry-brick masonry-brick-split';
32799         
32800         if(this.href.length){
32801             cls += ' masonry-brick-link';
32802         }
32803         
32804         if(this.bgimage.length){
32805             cls += ' masonry-brick-image';
32806         }
32807         
32808         if(this.size){
32809             cls += ' masonry-' + this.size + '-brick';
32810         }
32811         
32812         switch (this.placetitle) {
32813             case 'center' :
32814                 cls += ' masonry-center-title';
32815                 break;
32816             case 'bottom' :
32817                 cls += ' masonry-bottom-title';
32818                 break;
32819             default:
32820                 if(!this.bgimage.length){
32821                     cls += ' masonry-center-title';
32822                 }
32823
32824                 if(this.bgimage.length){
32825                     cls += ' masonry-bottom-title';
32826                 }
32827                 break;
32828         }
32829         
32830         if(this.cls){
32831             cls += ' ' + this.cls;
32832         }
32833         
32834         var cfg = {
32835             tag: (this.href.length) ? 'a' : 'div',
32836             cls: cls,
32837             cn: [
32838                 {
32839                     tag: 'div',
32840                     cls: 'masonry-brick-split-head',
32841                     cn: [
32842                         {
32843                             tag: 'div',
32844                             cls: 'masonry-brick-paragraph',
32845                             cn: []
32846                         }
32847                     ]
32848                 },
32849                 {
32850                     tag: 'div',
32851                     cls: 'masonry-brick-split-body',
32852                     cn: []
32853                 }
32854             ]
32855         };
32856         
32857         if(this.href.length){
32858             cfg.href = this.href;
32859         }
32860         
32861         if(this.title.length){
32862             cfg.cn[0].cn[0].cn.push({
32863                 tag: 'h4',
32864                 cls: 'masonry-brick-title',
32865                 html: this.title
32866             });
32867         }
32868         
32869         if(this.html.length){
32870             cfg.cn[1].cn.push({
32871                 tag: 'p',
32872                 cls: 'masonry-brick-text',
32873                 html: this.html
32874             });
32875         }
32876
32877         if(this.bgimage.length){
32878             cfg.cn[0].cn.push({
32879                 tag: 'img',
32880                 cls: 'masonry-brick-image-view',
32881                 src: this.bgimage
32882             });
32883         }
32884         
32885         if(this.videourl.length){
32886             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32887             // youtube support only?
32888             cfg.cn[0].cn.cn.push({
32889                 tag: 'iframe',
32890                 cls: 'masonry-brick-image-view',
32891                 src: vurl,
32892                 frameborder : 0,
32893                 allowfullscreen : true
32894             });
32895         }
32896         
32897         return cfg;
32898     },
32899     
32900     initEvents: function() 
32901     {
32902         switch (this.size) {
32903             case 'xs' :
32904                 this.x = 1;
32905                 this.y = 1;
32906                 break;
32907             case 'sm' :
32908                 this.x = 2;
32909                 this.y = 2;
32910                 break;
32911             case 'md' :
32912             case 'md-left' :
32913             case 'md-right' :
32914                 this.x = 3;
32915                 this.y = 3;
32916                 break;
32917             case 'tall' :
32918                 this.x = 2;
32919                 this.y = 3;
32920                 break;
32921             case 'wide' :
32922                 this.x = 3;
32923                 this.y = 2;
32924                 break;
32925             case 'wide-thin' :
32926                 this.x = 3;
32927                 this.y = 1;
32928                 break;
32929                         
32930             default :
32931                 break;
32932         }
32933         
32934         if(Roo.isTouch){
32935             this.el.on('touchstart', this.onTouchStart, this);
32936             this.el.on('touchmove', this.onTouchMove, this);
32937             this.el.on('touchend', this.onTouchEnd, this);
32938             this.el.on('contextmenu', this.onContextMenu, this);
32939         } else {
32940             this.el.on('mouseenter'  ,this.enter, this);
32941             this.el.on('mouseleave', this.leave, this);
32942             this.el.on('click', this.onClick, this);
32943         }
32944         
32945         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32946             this.parent().bricks.push(this);   
32947         }
32948         
32949     },
32950     
32951     onClick: function(e, el)
32952     {
32953         var time = this.endTimer - this.startTimer;
32954         // Roo.log(e.preventDefault());
32955         if(Roo.isTouch){
32956             if(time > 1000){
32957                 e.preventDefault();
32958                 return;
32959             }
32960         }
32961         
32962         if(!this.preventDefault){
32963             return;
32964         }
32965         
32966         e.preventDefault();
32967         
32968         if (this.activeClass != '') {
32969             this.selectBrick();
32970         }
32971         
32972         this.fireEvent('click', this, e);
32973     },
32974     
32975     enter: function(e, el)
32976     {
32977         e.preventDefault();
32978         
32979         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32980             return;
32981         }
32982         
32983         if(this.bgimage.length && this.html.length){
32984             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32985         }
32986     },
32987     
32988     leave: function(e, el)
32989     {
32990         e.preventDefault();
32991         
32992         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32993             return;
32994         }
32995         
32996         if(this.bgimage.length && this.html.length){
32997             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32998         }
32999     },
33000     
33001     onTouchStart: function(e, el)
33002     {
33003 //        e.preventDefault();
33004         
33005         this.touchmoved = false;
33006         
33007         if(!this.isFitContainer){
33008             return;
33009         }
33010         
33011         if(!this.bgimage.length || !this.html.length){
33012             return;
33013         }
33014         
33015         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33016         
33017         this.timer = new Date().getTime();
33018         
33019     },
33020     
33021     onTouchMove: function(e, el)
33022     {
33023         this.touchmoved = true;
33024     },
33025     
33026     onContextMenu : function(e,el)
33027     {
33028         e.preventDefault();
33029         e.stopPropagation();
33030         return false;
33031     },
33032     
33033     onTouchEnd: function(e, el)
33034     {
33035 //        e.preventDefault();
33036         
33037         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33038         
33039             this.leave(e,el);
33040             
33041             return;
33042         }
33043         
33044         if(!this.bgimage.length || !this.html.length){
33045             
33046             if(this.href.length){
33047                 window.location.href = this.href;
33048             }
33049             
33050             return;
33051         }
33052         
33053         if(!this.isFitContainer){
33054             return;
33055         }
33056         
33057         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33058         
33059         window.location.href = this.href;
33060     },
33061     
33062     //selection on single brick only
33063     selectBrick : function() {
33064         
33065         if (!this.parentId) {
33066             return;
33067         }
33068         
33069         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33070         var index = m.selectedBrick.indexOf(this.id);
33071         
33072         if ( index > -1) {
33073             m.selectedBrick.splice(index,1);
33074             this.el.removeClass(this.activeClass);
33075             return;
33076         }
33077         
33078         for(var i = 0; i < m.selectedBrick.length; i++) {
33079             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33080             b.el.removeClass(b.activeClass);
33081         }
33082         
33083         m.selectedBrick = [];
33084         
33085         m.selectedBrick.push(this.id);
33086         this.el.addClass(this.activeClass);
33087         return;
33088     },
33089     
33090     isSelected : function(){
33091         return this.el.hasClass(this.activeClass);
33092         
33093     }
33094 });
33095
33096 Roo.apply(Roo.bootstrap.MasonryBrick, {
33097     
33098     //groups: {},
33099     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33100      /**
33101     * register a Masonry Brick
33102     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33103     */
33104     
33105     register : function(brick)
33106     {
33107         //this.groups[brick.id] = brick;
33108         this.groups.add(brick.id, brick);
33109     },
33110     /**
33111     * fetch a  masonry brick based on the masonry brick ID
33112     * @param {string} the masonry brick to add
33113     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33114     */
33115     
33116     get: function(brick_id) 
33117     {
33118         // if (typeof(this.groups[brick_id]) == 'undefined') {
33119         //     return false;
33120         // }
33121         // return this.groups[brick_id] ;
33122         
33123         if(this.groups.key(brick_id)) {
33124             return this.groups.key(brick_id);
33125         }
33126         
33127         return false;
33128     }
33129     
33130     
33131     
33132 });
33133
33134  /*
33135  * - LGPL
33136  *
33137  * element
33138  * 
33139  */
33140
33141 /**
33142  * @class Roo.bootstrap.Brick
33143  * @extends Roo.bootstrap.Component
33144  * Bootstrap Brick class
33145  * 
33146  * @constructor
33147  * Create a new Brick
33148  * @param {Object} config The config object
33149  */
33150
33151 Roo.bootstrap.Brick = function(config){
33152     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33153     
33154     this.addEvents({
33155         // raw events
33156         /**
33157          * @event click
33158          * When a Brick is click
33159          * @param {Roo.bootstrap.Brick} this
33160          * @param {Roo.EventObject} e
33161          */
33162         "click" : true
33163     });
33164 };
33165
33166 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33167     
33168     /**
33169      * @cfg {String} title
33170      */   
33171     title : '',
33172     /**
33173      * @cfg {String} html
33174      */   
33175     html : '',
33176     /**
33177      * @cfg {String} bgimage
33178      */   
33179     bgimage : '',
33180     /**
33181      * @cfg {String} cls
33182      */   
33183     cls : '',
33184     /**
33185      * @cfg {String} href
33186      */   
33187     href : '',
33188     /**
33189      * @cfg {String} video
33190      */   
33191     video : '',
33192     /**
33193      * @cfg {Boolean} square
33194      */   
33195     square : true,
33196     
33197     getAutoCreate : function()
33198     {
33199         var cls = 'roo-brick';
33200         
33201         if(this.href.length){
33202             cls += ' roo-brick-link';
33203         }
33204         
33205         if(this.bgimage.length){
33206             cls += ' roo-brick-image';
33207         }
33208         
33209         if(!this.html.length && !this.bgimage.length){
33210             cls += ' roo-brick-center-title';
33211         }
33212         
33213         if(!this.html.length && this.bgimage.length){
33214             cls += ' roo-brick-bottom-title';
33215         }
33216         
33217         if(this.cls){
33218             cls += ' ' + this.cls;
33219         }
33220         
33221         var cfg = {
33222             tag: (this.href.length) ? 'a' : 'div',
33223             cls: cls,
33224             cn: [
33225                 {
33226                     tag: 'div',
33227                     cls: 'roo-brick-paragraph',
33228                     cn: []
33229                 }
33230             ]
33231         };
33232         
33233         if(this.href.length){
33234             cfg.href = this.href;
33235         }
33236         
33237         var cn = cfg.cn[0].cn;
33238         
33239         if(this.title.length){
33240             cn.push({
33241                 tag: 'h4',
33242                 cls: 'roo-brick-title',
33243                 html: this.title
33244             });
33245         }
33246         
33247         if(this.html.length){
33248             cn.push({
33249                 tag: 'p',
33250                 cls: 'roo-brick-text',
33251                 html: this.html
33252             });
33253         } else {
33254             cn.cls += ' hide';
33255         }
33256         
33257         if(this.bgimage.length){
33258             cfg.cn.push({
33259                 tag: 'img',
33260                 cls: 'roo-brick-image-view',
33261                 src: this.bgimage
33262             });
33263         }
33264         
33265         return cfg;
33266     },
33267     
33268     initEvents: function() 
33269     {
33270         if(this.title.length || this.html.length){
33271             this.el.on('mouseenter'  ,this.enter, this);
33272             this.el.on('mouseleave', this.leave, this);
33273         }
33274         
33275         Roo.EventManager.onWindowResize(this.resize, this); 
33276         
33277         if(this.bgimage.length){
33278             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33279             this.imageEl.on('load', this.onImageLoad, this);
33280             return;
33281         }
33282         
33283         this.resize();
33284     },
33285     
33286     onImageLoad : function()
33287     {
33288         this.resize();
33289     },
33290     
33291     resize : function()
33292     {
33293         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33294         
33295         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33296         
33297         if(this.bgimage.length){
33298             var image = this.el.select('.roo-brick-image-view', true).first();
33299             
33300             image.setWidth(paragraph.getWidth());
33301             
33302             if(this.square){
33303                 image.setHeight(paragraph.getWidth());
33304             }
33305             
33306             this.el.setHeight(image.getHeight());
33307             paragraph.setHeight(image.getHeight());
33308             
33309         }
33310         
33311     },
33312     
33313     enter: function(e, el)
33314     {
33315         e.preventDefault();
33316         
33317         if(this.bgimage.length){
33318             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33319             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33320         }
33321     },
33322     
33323     leave: function(e, el)
33324     {
33325         e.preventDefault();
33326         
33327         if(this.bgimage.length){
33328             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33329             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33330         }
33331     }
33332     
33333 });
33334
33335  
33336
33337  /*
33338  * - LGPL
33339  *
33340  * Number field 
33341  */
33342
33343 /**
33344  * @class Roo.bootstrap.NumberField
33345  * @extends Roo.bootstrap.Input
33346  * Bootstrap NumberField class
33347  * 
33348  * 
33349  * 
33350  * 
33351  * @constructor
33352  * Create a new NumberField
33353  * @param {Object} config The config object
33354  */
33355
33356 Roo.bootstrap.NumberField = function(config){
33357     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33358 };
33359
33360 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33361     
33362     /**
33363      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33364      */
33365     allowDecimals : true,
33366     /**
33367      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33368      */
33369     decimalSeparator : ".",
33370     /**
33371      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33372      */
33373     decimalPrecision : 2,
33374     /**
33375      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33376      */
33377     allowNegative : true,
33378     
33379     /**
33380      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33381      */
33382     allowZero: true,
33383     /**
33384      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33385      */
33386     minValue : Number.NEGATIVE_INFINITY,
33387     /**
33388      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33389      */
33390     maxValue : Number.MAX_VALUE,
33391     /**
33392      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33393      */
33394     minText : "The minimum value for this field is {0}",
33395     /**
33396      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33397      */
33398     maxText : "The maximum value for this field is {0}",
33399     /**
33400      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33401      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33402      */
33403     nanText : "{0} is not a valid number",
33404     /**
33405      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33406      */
33407     thousandsDelimiter : false,
33408     /**
33409      * @cfg {String} valueAlign alignment of value
33410      */
33411     valueAlign : "left",
33412
33413     getAutoCreate : function()
33414     {
33415         var hiddenInput = {
33416             tag: 'input',
33417             type: 'hidden',
33418             id: Roo.id(),
33419             cls: 'hidden-number-input'
33420         };
33421         
33422         if (this.name) {
33423             hiddenInput.name = this.name;
33424         }
33425         
33426         this.name = '';
33427         
33428         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33429         
33430         this.name = hiddenInput.name;
33431         
33432         if(cfg.cn.length > 0) {
33433             cfg.cn.push(hiddenInput);
33434         }
33435         
33436         return cfg;
33437     },
33438
33439     // private
33440     initEvents : function()
33441     {   
33442         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33443         
33444         var allowed = "0123456789";
33445         
33446         if(this.allowDecimals){
33447             allowed += this.decimalSeparator;
33448         }
33449         
33450         if(this.allowNegative){
33451             allowed += "-";
33452         }
33453         
33454         if(this.thousandsDelimiter) {
33455             allowed += ",";
33456         }
33457         
33458         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33459         
33460         var keyPress = function(e){
33461             
33462             var k = e.getKey();
33463             
33464             var c = e.getCharCode();
33465             
33466             if(
33467                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33468                     allowed.indexOf(String.fromCharCode(c)) === -1
33469             ){
33470                 e.stopEvent();
33471                 return;
33472             }
33473             
33474             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33475                 return;
33476             }
33477             
33478             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33479                 e.stopEvent();
33480             }
33481         };
33482         
33483         this.el.on("keypress", keyPress, this);
33484     },
33485     
33486     validateValue : function(value)
33487     {
33488         
33489         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33490             return false;
33491         }
33492         
33493         var num = this.parseValue(value);
33494         
33495         if(isNaN(num)){
33496             this.markInvalid(String.format(this.nanText, value));
33497             return false;
33498         }
33499         
33500         if(num < this.minValue){
33501             this.markInvalid(String.format(this.minText, this.minValue));
33502             return false;
33503         }
33504         
33505         if(num > this.maxValue){
33506             this.markInvalid(String.format(this.maxText, this.maxValue));
33507             return false;
33508         }
33509         
33510         return true;
33511     },
33512
33513     getValue : function()
33514     {
33515         var v = this.hiddenEl().getValue();
33516         
33517         return this.fixPrecision(this.parseValue(v));
33518     },
33519
33520     parseValue : function(value)
33521     {
33522         if(this.thousandsDelimiter) {
33523             value += "";
33524             r = new RegExp(",", "g");
33525             value = value.replace(r, "");
33526         }
33527         
33528         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33529         return isNaN(value) ? '' : value;
33530     },
33531
33532     fixPrecision : function(value)
33533     {
33534         if(this.thousandsDelimiter) {
33535             value += "";
33536             r = new RegExp(",", "g");
33537             value = value.replace(r, "");
33538         }
33539         
33540         var nan = isNaN(value);
33541         
33542         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33543             return nan ? '' : value;
33544         }
33545         return parseFloat(value).toFixed(this.decimalPrecision);
33546     },
33547
33548     setValue : function(v)
33549     {
33550         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33551         
33552         this.value = v;
33553         
33554         if(this.rendered){
33555             
33556             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33557             
33558             this.inputEl().dom.value = (v == '') ? '' :
33559                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33560             
33561             if(!this.allowZero && v === '0') {
33562                 this.hiddenEl().dom.value = '';
33563                 this.inputEl().dom.value = '';
33564             }
33565             
33566             this.validate();
33567         }
33568     },
33569
33570     decimalPrecisionFcn : function(v)
33571     {
33572         return Math.floor(v);
33573     },
33574
33575     beforeBlur : function()
33576     {
33577         var v = this.parseValue(this.getRawValue());
33578         
33579         if(v || v === 0 || v === ''){
33580             this.setValue(v);
33581         }
33582     },
33583     
33584     hiddenEl : function()
33585     {
33586         return this.el.select('input.hidden-number-input',true).first();
33587     }
33588     
33589 });
33590
33591  
33592
33593 /*
33594 * Licence: LGPL
33595 */
33596
33597 /**
33598  * @class Roo.bootstrap.DocumentSlider
33599  * @extends Roo.bootstrap.Component
33600  * Bootstrap DocumentSlider class
33601  * 
33602  * @constructor
33603  * Create a new DocumentViewer
33604  * @param {Object} config The config object
33605  */
33606
33607 Roo.bootstrap.DocumentSlider = function(config){
33608     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33609     
33610     this.files = [];
33611     
33612     this.addEvents({
33613         /**
33614          * @event initial
33615          * Fire after initEvent
33616          * @param {Roo.bootstrap.DocumentSlider} this
33617          */
33618         "initial" : true,
33619         /**
33620          * @event update
33621          * Fire after update
33622          * @param {Roo.bootstrap.DocumentSlider} this
33623          */
33624         "update" : true,
33625         /**
33626          * @event click
33627          * Fire after click
33628          * @param {Roo.bootstrap.DocumentSlider} this
33629          */
33630         "click" : true
33631     });
33632 };
33633
33634 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33635     
33636     files : false,
33637     
33638     indicator : 0,
33639     
33640     getAutoCreate : function()
33641     {
33642         var cfg = {
33643             tag : 'div',
33644             cls : 'roo-document-slider',
33645             cn : [
33646                 {
33647                     tag : 'div',
33648                     cls : 'roo-document-slider-header',
33649                     cn : [
33650                         {
33651                             tag : 'div',
33652                             cls : 'roo-document-slider-header-title'
33653                         }
33654                     ]
33655                 },
33656                 {
33657                     tag : 'div',
33658                     cls : 'roo-document-slider-body',
33659                     cn : [
33660                         {
33661                             tag : 'div',
33662                             cls : 'roo-document-slider-prev',
33663                             cn : [
33664                                 {
33665                                     tag : 'i',
33666                                     cls : 'fa fa-chevron-left'
33667                                 }
33668                             ]
33669                         },
33670                         {
33671                             tag : 'div',
33672                             cls : 'roo-document-slider-thumb',
33673                             cn : [
33674                                 {
33675                                     tag : 'img',
33676                                     cls : 'roo-document-slider-image'
33677                                 }
33678                             ]
33679                         },
33680                         {
33681                             tag : 'div',
33682                             cls : 'roo-document-slider-next',
33683                             cn : [
33684                                 {
33685                                     tag : 'i',
33686                                     cls : 'fa fa-chevron-right'
33687                                 }
33688                             ]
33689                         }
33690                     ]
33691                 }
33692             ]
33693         };
33694         
33695         return cfg;
33696     },
33697     
33698     initEvents : function()
33699     {
33700         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33701         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33702         
33703         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33704         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33705         
33706         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33707         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33708         
33709         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33710         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33711         
33712         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33713         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33714         
33715         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33716         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33717         
33718         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33719         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33720         
33721         this.thumbEl.on('click', this.onClick, this);
33722         
33723         this.prevIndicator.on('click', this.prev, this);
33724         
33725         this.nextIndicator.on('click', this.next, this);
33726         
33727     },
33728     
33729     initial : function()
33730     {
33731         if(this.files.length){
33732             this.indicator = 1;
33733             this.update()
33734         }
33735         
33736         this.fireEvent('initial', this);
33737     },
33738     
33739     update : function()
33740     {
33741         this.imageEl.attr('src', this.files[this.indicator - 1]);
33742         
33743         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33744         
33745         this.prevIndicator.show();
33746         
33747         if(this.indicator == 1){
33748             this.prevIndicator.hide();
33749         }
33750         
33751         this.nextIndicator.show();
33752         
33753         if(this.indicator == this.files.length){
33754             this.nextIndicator.hide();
33755         }
33756         
33757         this.thumbEl.scrollTo('top');
33758         
33759         this.fireEvent('update', this);
33760     },
33761     
33762     onClick : function(e)
33763     {
33764         e.preventDefault();
33765         
33766         this.fireEvent('click', this);
33767     },
33768     
33769     prev : function(e)
33770     {
33771         e.preventDefault();
33772         
33773         this.indicator = Math.max(1, this.indicator - 1);
33774         
33775         this.update();
33776     },
33777     
33778     next : function(e)
33779     {
33780         e.preventDefault();
33781         
33782         this.indicator = Math.min(this.files.length, this.indicator + 1);
33783         
33784         this.update();
33785     }
33786 });
33787 /*
33788  * - LGPL
33789  *
33790  * RadioSet
33791  *
33792  *
33793  */
33794
33795 /**
33796  * @class Roo.bootstrap.RadioSet
33797  * @extends Roo.bootstrap.Input
33798  * Bootstrap RadioSet class
33799  * @cfg {String} indicatorpos (left|right) default left
33800  * @cfg {Boolean} inline (true|false) inline the element (default true)
33801  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33802  * @constructor
33803  * Create a new RadioSet
33804  * @param {Object} config The config object
33805  */
33806
33807 Roo.bootstrap.RadioSet = function(config){
33808     
33809     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33810     
33811     this.radioes = [];
33812     
33813     Roo.bootstrap.RadioSet.register(this);
33814     
33815     this.addEvents({
33816         /**
33817         * @event check
33818         * Fires when the element is checked or unchecked.
33819         * @param {Roo.bootstrap.RadioSet} this This radio
33820         * @param {Roo.bootstrap.Radio} item The checked item
33821         */
33822        check : true,
33823        /**
33824         * @event click
33825         * Fires when the element is click.
33826         * @param {Roo.bootstrap.RadioSet} this This radio set
33827         * @param {Roo.bootstrap.Radio} item The checked item
33828         * @param {Roo.EventObject} e The event object
33829         */
33830        click : true
33831     });
33832     
33833 };
33834
33835 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33836
33837     radioes : false,
33838     
33839     inline : true,
33840     
33841     weight : '',
33842     
33843     indicatorpos : 'left',
33844     
33845     getAutoCreate : function()
33846     {
33847         var label = {
33848             tag : 'label',
33849             cls : 'roo-radio-set-label',
33850             cn : [
33851                 {
33852                     tag : 'span',
33853                     html : this.fieldLabel
33854                 }
33855             ]
33856         };
33857         
33858         if(this.indicatorpos == 'left'){
33859             label.cn.unshift({
33860                 tag : 'i',
33861                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33862                 tooltip : 'This field is required'
33863             });
33864         } else {
33865             label.cn.push({
33866                 tag : 'i',
33867                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33868                 tooltip : 'This field is required'
33869             });
33870         }
33871         
33872         var items = {
33873             tag : 'div',
33874             cls : 'roo-radio-set-items'
33875         };
33876         
33877         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33878         
33879         if (align === 'left' && this.fieldLabel.length) {
33880             
33881             items = {
33882                 cls : "roo-radio-set-right", 
33883                 cn: [
33884                     items
33885                 ]
33886             };
33887             
33888             if(this.labelWidth > 12){
33889                 label.style = "width: " + this.labelWidth + 'px';
33890             }
33891             
33892             if(this.labelWidth < 13 && this.labelmd == 0){
33893                 this.labelmd = this.labelWidth;
33894             }
33895             
33896             if(this.labellg > 0){
33897                 label.cls += ' col-lg-' + this.labellg;
33898                 items.cls += ' col-lg-' + (12 - this.labellg);
33899             }
33900             
33901             if(this.labelmd > 0){
33902                 label.cls += ' col-md-' + this.labelmd;
33903                 items.cls += ' col-md-' + (12 - this.labelmd);
33904             }
33905             
33906             if(this.labelsm > 0){
33907                 label.cls += ' col-sm-' + this.labelsm;
33908                 items.cls += ' col-sm-' + (12 - this.labelsm);
33909             }
33910             
33911             if(this.labelxs > 0){
33912                 label.cls += ' col-xs-' + this.labelxs;
33913                 items.cls += ' col-xs-' + (12 - this.labelxs);
33914             }
33915         }
33916         
33917         var cfg = {
33918             tag : 'div',
33919             cls : 'roo-radio-set',
33920             cn : [
33921                 {
33922                     tag : 'input',
33923                     cls : 'roo-radio-set-input',
33924                     type : 'hidden',
33925                     name : this.name,
33926                     value : this.value ? this.value :  ''
33927                 },
33928                 label,
33929                 items
33930             ]
33931         };
33932         
33933         if(this.weight.length){
33934             cfg.cls += ' roo-radio-' + this.weight;
33935         }
33936         
33937         if(this.inline) {
33938             cfg.cls += ' roo-radio-set-inline';
33939         }
33940         
33941         var settings=this;
33942         ['xs','sm','md','lg'].map(function(size){
33943             if (settings[size]) {
33944                 cfg.cls += ' col-' + size + '-' + settings[size];
33945             }
33946         });
33947         
33948         return cfg;
33949         
33950     },
33951
33952     initEvents : function()
33953     {
33954         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33955         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33956         
33957         if(!this.fieldLabel.length){
33958             this.labelEl.hide();
33959         }
33960         
33961         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33962         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33963         
33964         this.indicator = this.indicatorEl();
33965         
33966         if(this.indicator){
33967             this.indicator.addClass('invisible');
33968         }
33969         
33970         this.originalValue = this.getValue();
33971         
33972     },
33973     
33974     inputEl: function ()
33975     {
33976         return this.el.select('.roo-radio-set-input', true).first();
33977     },
33978     
33979     getChildContainer : function()
33980     {
33981         return this.itemsEl;
33982     },
33983     
33984     register : function(item)
33985     {
33986         this.radioes.push(item);
33987         
33988     },
33989     
33990     validate : function()
33991     {   
33992         if(this.getVisibilityEl().hasClass('hidden')){
33993             return true;
33994         }
33995         
33996         var valid = false;
33997         
33998         Roo.each(this.radioes, function(i){
33999             if(!i.checked){
34000                 return;
34001             }
34002             
34003             valid = true;
34004             return false;
34005         });
34006         
34007         if(this.allowBlank) {
34008             return true;
34009         }
34010         
34011         if(this.disabled || valid){
34012             this.markValid();
34013             return true;
34014         }
34015         
34016         this.markInvalid();
34017         return false;
34018         
34019     },
34020     
34021     markValid : function()
34022     {
34023         if(this.labelEl.isVisible(true)){
34024             this.indicatorEl().removeClass('visible');
34025             this.indicatorEl().addClass('invisible');
34026         }
34027         
34028         this.el.removeClass([this.invalidClass, this.validClass]);
34029         this.el.addClass(this.validClass);
34030         
34031         this.fireEvent('valid', this);
34032     },
34033     
34034     markInvalid : function(msg)
34035     {
34036         if(this.allowBlank || this.disabled){
34037             return;
34038         }
34039         
34040         if(this.labelEl.isVisible(true)){
34041             this.indicatorEl().removeClass('invisible');
34042             this.indicatorEl().addClass('visible');
34043         }
34044         
34045         this.el.removeClass([this.invalidClass, this.validClass]);
34046         this.el.addClass(this.invalidClass);
34047         
34048         this.fireEvent('invalid', this, msg);
34049         
34050     },
34051     
34052     setValue : function(v, suppressEvent)
34053     {   
34054         if(this.value === v){
34055             return;
34056         }
34057         
34058         this.value = v;
34059         
34060         if(this.rendered){
34061             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34062         }
34063         
34064         Roo.each(this.radioes, function(i){
34065             i.checked = false;
34066             i.el.removeClass('checked');
34067         });
34068         
34069         Roo.each(this.radioes, function(i){
34070             
34071             if(i.value === v || i.value.toString() === v.toString()){
34072                 i.checked = true;
34073                 i.el.addClass('checked');
34074                 
34075                 if(suppressEvent !== true){
34076                     this.fireEvent('check', this, i);
34077                 }
34078                 
34079                 return false;
34080             }
34081             
34082         }, this);
34083         
34084         this.validate();
34085     },
34086     
34087     clearInvalid : function(){
34088         
34089         if(!this.el || this.preventMark){
34090             return;
34091         }
34092         
34093         this.el.removeClass([this.invalidClass]);
34094         
34095         this.fireEvent('valid', this);
34096     }
34097     
34098 });
34099
34100 Roo.apply(Roo.bootstrap.RadioSet, {
34101     
34102     groups: {},
34103     
34104     register : function(set)
34105     {
34106         this.groups[set.name] = set;
34107     },
34108     
34109     get: function(name) 
34110     {
34111         if (typeof(this.groups[name]) == 'undefined') {
34112             return false;
34113         }
34114         
34115         return this.groups[name] ;
34116     }
34117     
34118 });
34119 /*
34120  * Based on:
34121  * Ext JS Library 1.1.1
34122  * Copyright(c) 2006-2007, Ext JS, LLC.
34123  *
34124  * Originally Released Under LGPL - original licence link has changed is not relivant.
34125  *
34126  * Fork - LGPL
34127  * <script type="text/javascript">
34128  */
34129
34130
34131 /**
34132  * @class Roo.bootstrap.SplitBar
34133  * @extends Roo.util.Observable
34134  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34135  * <br><br>
34136  * Usage:
34137  * <pre><code>
34138 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34139                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34140 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34141 split.minSize = 100;
34142 split.maxSize = 600;
34143 split.animate = true;
34144 split.on('moved', splitterMoved);
34145 </code></pre>
34146  * @constructor
34147  * Create a new SplitBar
34148  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34149  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34150  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34151  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34152                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34153                         position of the SplitBar).
34154  */
34155 Roo.bootstrap.SplitBar = function(cfg){
34156     
34157     /** @private */
34158     
34159     //{
34160     //  dragElement : elm
34161     //  resizingElement: el,
34162         // optional..
34163     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34164     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34165         // existingProxy ???
34166     //}
34167     
34168     this.el = Roo.get(cfg.dragElement, true);
34169     this.el.dom.unselectable = "on";
34170     /** @private */
34171     this.resizingEl = Roo.get(cfg.resizingElement, true);
34172
34173     /**
34174      * @private
34175      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34176      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34177      * @type Number
34178      */
34179     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34180     
34181     /**
34182      * The minimum size of the resizing element. (Defaults to 0)
34183      * @type Number
34184      */
34185     this.minSize = 0;
34186     
34187     /**
34188      * The maximum size of the resizing element. (Defaults to 2000)
34189      * @type Number
34190      */
34191     this.maxSize = 2000;
34192     
34193     /**
34194      * Whether to animate the transition to the new size
34195      * @type Boolean
34196      */
34197     this.animate = false;
34198     
34199     /**
34200      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34201      * @type Boolean
34202      */
34203     this.useShim = false;
34204     
34205     /** @private */
34206     this.shim = null;
34207     
34208     if(!cfg.existingProxy){
34209         /** @private */
34210         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34211     }else{
34212         this.proxy = Roo.get(cfg.existingProxy).dom;
34213     }
34214     /** @private */
34215     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34216     
34217     /** @private */
34218     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34219     
34220     /** @private */
34221     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34222     
34223     /** @private */
34224     this.dragSpecs = {};
34225     
34226     /**
34227      * @private The adapter to use to positon and resize elements
34228      */
34229     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34230     this.adapter.init(this);
34231     
34232     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34233         /** @private */
34234         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34235         this.el.addClass("roo-splitbar-h");
34236     }else{
34237         /** @private */
34238         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34239         this.el.addClass("roo-splitbar-v");
34240     }
34241     
34242     this.addEvents({
34243         /**
34244          * @event resize
34245          * Fires when the splitter is moved (alias for {@link #event-moved})
34246          * @param {Roo.bootstrap.SplitBar} this
34247          * @param {Number} newSize the new width or height
34248          */
34249         "resize" : true,
34250         /**
34251          * @event moved
34252          * Fires when the splitter is moved
34253          * @param {Roo.bootstrap.SplitBar} this
34254          * @param {Number} newSize the new width or height
34255          */
34256         "moved" : true,
34257         /**
34258          * @event beforeresize
34259          * Fires before the splitter is dragged
34260          * @param {Roo.bootstrap.SplitBar} this
34261          */
34262         "beforeresize" : true,
34263
34264         "beforeapply" : true
34265     });
34266
34267     Roo.util.Observable.call(this);
34268 };
34269
34270 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34271     onStartProxyDrag : function(x, y){
34272         this.fireEvent("beforeresize", this);
34273         if(!this.overlay){
34274             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34275             o.unselectable();
34276             o.enableDisplayMode("block");
34277             // all splitbars share the same overlay
34278             Roo.bootstrap.SplitBar.prototype.overlay = o;
34279         }
34280         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34281         this.overlay.show();
34282         Roo.get(this.proxy).setDisplayed("block");
34283         var size = this.adapter.getElementSize(this);
34284         this.activeMinSize = this.getMinimumSize();;
34285         this.activeMaxSize = this.getMaximumSize();;
34286         var c1 = size - this.activeMinSize;
34287         var c2 = Math.max(this.activeMaxSize - size, 0);
34288         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34289             this.dd.resetConstraints();
34290             this.dd.setXConstraint(
34291                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34292                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34293             );
34294             this.dd.setYConstraint(0, 0);
34295         }else{
34296             this.dd.resetConstraints();
34297             this.dd.setXConstraint(0, 0);
34298             this.dd.setYConstraint(
34299                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34300                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34301             );
34302          }
34303         this.dragSpecs.startSize = size;
34304         this.dragSpecs.startPoint = [x, y];
34305         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34306     },
34307     
34308     /** 
34309      * @private Called after the drag operation by the DDProxy
34310      */
34311     onEndProxyDrag : function(e){
34312         Roo.get(this.proxy).setDisplayed(false);
34313         var endPoint = Roo.lib.Event.getXY(e);
34314         if(this.overlay){
34315             this.overlay.hide();
34316         }
34317         var newSize;
34318         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34319             newSize = this.dragSpecs.startSize + 
34320                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34321                     endPoint[0] - this.dragSpecs.startPoint[0] :
34322                     this.dragSpecs.startPoint[0] - endPoint[0]
34323                 );
34324         }else{
34325             newSize = this.dragSpecs.startSize + 
34326                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34327                     endPoint[1] - this.dragSpecs.startPoint[1] :
34328                     this.dragSpecs.startPoint[1] - endPoint[1]
34329                 );
34330         }
34331         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34332         if(newSize != this.dragSpecs.startSize){
34333             if(this.fireEvent('beforeapply', this, newSize) !== false){
34334                 this.adapter.setElementSize(this, newSize);
34335                 this.fireEvent("moved", this, newSize);
34336                 this.fireEvent("resize", this, newSize);
34337             }
34338         }
34339     },
34340     
34341     /**
34342      * Get the adapter this SplitBar uses
34343      * @return The adapter object
34344      */
34345     getAdapter : function(){
34346         return this.adapter;
34347     },
34348     
34349     /**
34350      * Set the adapter this SplitBar uses
34351      * @param {Object} adapter A SplitBar adapter object
34352      */
34353     setAdapter : function(adapter){
34354         this.adapter = adapter;
34355         this.adapter.init(this);
34356     },
34357     
34358     /**
34359      * Gets the minimum size for the resizing element
34360      * @return {Number} The minimum size
34361      */
34362     getMinimumSize : function(){
34363         return this.minSize;
34364     },
34365     
34366     /**
34367      * Sets the minimum size for the resizing element
34368      * @param {Number} minSize The minimum size
34369      */
34370     setMinimumSize : function(minSize){
34371         this.minSize = minSize;
34372     },
34373     
34374     /**
34375      * Gets the maximum size for the resizing element
34376      * @return {Number} The maximum size
34377      */
34378     getMaximumSize : function(){
34379         return this.maxSize;
34380     },
34381     
34382     /**
34383      * Sets the maximum size for the resizing element
34384      * @param {Number} maxSize The maximum size
34385      */
34386     setMaximumSize : function(maxSize){
34387         this.maxSize = maxSize;
34388     },
34389     
34390     /**
34391      * Sets the initialize size for the resizing element
34392      * @param {Number} size The initial size
34393      */
34394     setCurrentSize : function(size){
34395         var oldAnimate = this.animate;
34396         this.animate = false;
34397         this.adapter.setElementSize(this, size);
34398         this.animate = oldAnimate;
34399     },
34400     
34401     /**
34402      * Destroy this splitbar. 
34403      * @param {Boolean} removeEl True to remove the element
34404      */
34405     destroy : function(removeEl){
34406         if(this.shim){
34407             this.shim.remove();
34408         }
34409         this.dd.unreg();
34410         this.proxy.parentNode.removeChild(this.proxy);
34411         if(removeEl){
34412             this.el.remove();
34413         }
34414     }
34415 });
34416
34417 /**
34418  * @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.
34419  */
34420 Roo.bootstrap.SplitBar.createProxy = function(dir){
34421     var proxy = new Roo.Element(document.createElement("div"));
34422     proxy.unselectable();
34423     var cls = 'roo-splitbar-proxy';
34424     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34425     document.body.appendChild(proxy.dom);
34426     return proxy.dom;
34427 };
34428
34429 /** 
34430  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34431  * Default Adapter. It assumes the splitter and resizing element are not positioned
34432  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34433  */
34434 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34435 };
34436
34437 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34438     // do nothing for now
34439     init : function(s){
34440     
34441     },
34442     /**
34443      * Called before drag operations to get the current size of the resizing element. 
34444      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34445      */
34446      getElementSize : function(s){
34447         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34448             return s.resizingEl.getWidth();
34449         }else{
34450             return s.resizingEl.getHeight();
34451         }
34452     },
34453     
34454     /**
34455      * Called after drag operations to set the size of the resizing element.
34456      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34457      * @param {Number} newSize The new size to set
34458      * @param {Function} onComplete A function to be invoked when resizing is complete
34459      */
34460     setElementSize : function(s, newSize, onComplete){
34461         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34462             if(!s.animate){
34463                 s.resizingEl.setWidth(newSize);
34464                 if(onComplete){
34465                     onComplete(s, newSize);
34466                 }
34467             }else{
34468                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34469             }
34470         }else{
34471             
34472             if(!s.animate){
34473                 s.resizingEl.setHeight(newSize);
34474                 if(onComplete){
34475                     onComplete(s, newSize);
34476                 }
34477             }else{
34478                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34479             }
34480         }
34481     }
34482 };
34483
34484 /** 
34485  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34486  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34487  * Adapter that  moves the splitter element to align with the resized sizing element. 
34488  * Used with an absolute positioned SplitBar.
34489  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34490  * document.body, make sure you assign an id to the body element.
34491  */
34492 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34493     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34494     this.container = Roo.get(container);
34495 };
34496
34497 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34498     init : function(s){
34499         this.basic.init(s);
34500     },
34501     
34502     getElementSize : function(s){
34503         return this.basic.getElementSize(s);
34504     },
34505     
34506     setElementSize : function(s, newSize, onComplete){
34507         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34508     },
34509     
34510     moveSplitter : function(s){
34511         var yes = Roo.bootstrap.SplitBar;
34512         switch(s.placement){
34513             case yes.LEFT:
34514                 s.el.setX(s.resizingEl.getRight());
34515                 break;
34516             case yes.RIGHT:
34517                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34518                 break;
34519             case yes.TOP:
34520                 s.el.setY(s.resizingEl.getBottom());
34521                 break;
34522             case yes.BOTTOM:
34523                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34524                 break;
34525         }
34526     }
34527 };
34528
34529 /**
34530  * Orientation constant - Create a vertical SplitBar
34531  * @static
34532  * @type Number
34533  */
34534 Roo.bootstrap.SplitBar.VERTICAL = 1;
34535
34536 /**
34537  * Orientation constant - Create a horizontal SplitBar
34538  * @static
34539  * @type Number
34540  */
34541 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34542
34543 /**
34544  * Placement constant - The resizing element is to the left of the splitter element
34545  * @static
34546  * @type Number
34547  */
34548 Roo.bootstrap.SplitBar.LEFT = 1;
34549
34550 /**
34551  * Placement constant - The resizing element is to the right of the splitter element
34552  * @static
34553  * @type Number
34554  */
34555 Roo.bootstrap.SplitBar.RIGHT = 2;
34556
34557 /**
34558  * Placement constant - The resizing element is positioned above the splitter element
34559  * @static
34560  * @type Number
34561  */
34562 Roo.bootstrap.SplitBar.TOP = 3;
34563
34564 /**
34565  * Placement constant - The resizing element is positioned under splitter element
34566  * @static
34567  * @type Number
34568  */
34569 Roo.bootstrap.SplitBar.BOTTOM = 4;
34570 Roo.namespace("Roo.bootstrap.layout");/*
34571  * Based on:
34572  * Ext JS Library 1.1.1
34573  * Copyright(c) 2006-2007, Ext JS, LLC.
34574  *
34575  * Originally Released Under LGPL - original licence link has changed is not relivant.
34576  *
34577  * Fork - LGPL
34578  * <script type="text/javascript">
34579  */
34580
34581 /**
34582  * @class Roo.bootstrap.layout.Manager
34583  * @extends Roo.bootstrap.Component
34584  * Base class for layout managers.
34585  */
34586 Roo.bootstrap.layout.Manager = function(config)
34587 {
34588     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34589
34590
34591
34592
34593
34594     /** false to disable window resize monitoring @type Boolean */
34595     this.monitorWindowResize = true;
34596     this.regions = {};
34597     this.addEvents({
34598         /**
34599          * @event layout
34600          * Fires when a layout is performed.
34601          * @param {Roo.LayoutManager} this
34602          */
34603         "layout" : true,
34604         /**
34605          * @event regionresized
34606          * Fires when the user resizes a region.
34607          * @param {Roo.LayoutRegion} region The resized region
34608          * @param {Number} newSize The new size (width for east/west, height for north/south)
34609          */
34610         "regionresized" : true,
34611         /**
34612          * @event regioncollapsed
34613          * Fires when a region is collapsed.
34614          * @param {Roo.LayoutRegion} region The collapsed region
34615          */
34616         "regioncollapsed" : true,
34617         /**
34618          * @event regionexpanded
34619          * Fires when a region is expanded.
34620          * @param {Roo.LayoutRegion} region The expanded region
34621          */
34622         "regionexpanded" : true
34623     });
34624     this.updating = false;
34625
34626     if (config.el) {
34627         this.el = Roo.get(config.el);
34628         this.initEvents();
34629     }
34630
34631 };
34632
34633 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34634
34635
34636     regions : null,
34637
34638     monitorWindowResize : true,
34639
34640
34641     updating : false,
34642
34643
34644     onRender : function(ct, position)
34645     {
34646         if(!this.el){
34647             this.el = Roo.get(ct);
34648             this.initEvents();
34649         }
34650         //this.fireEvent('render',this);
34651     },
34652
34653
34654     initEvents: function()
34655     {
34656
34657
34658         // ie scrollbar fix
34659         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34660             document.body.scroll = "no";
34661         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34662             this.el.position('relative');
34663         }
34664         this.id = this.el.id;
34665         this.el.addClass("roo-layout-container");
34666         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34667         if(this.el.dom != document.body ) {
34668             this.el.on('resize', this.layout,this);
34669             this.el.on('show', this.layout,this);
34670         }
34671
34672     },
34673
34674     /**
34675      * Returns true if this layout is currently being updated
34676      * @return {Boolean}
34677      */
34678     isUpdating : function(){
34679         return this.updating;
34680     },
34681
34682     /**
34683      * Suspend the LayoutManager from doing auto-layouts while
34684      * making multiple add or remove calls
34685      */
34686     beginUpdate : function(){
34687         this.updating = true;
34688     },
34689
34690     /**
34691      * Restore auto-layouts and optionally disable the manager from performing a layout
34692      * @param {Boolean} noLayout true to disable a layout update
34693      */
34694     endUpdate : function(noLayout){
34695         this.updating = false;
34696         if(!noLayout){
34697             this.layout();
34698         }
34699     },
34700
34701     layout: function(){
34702         // abstract...
34703     },
34704
34705     onRegionResized : function(region, newSize){
34706         this.fireEvent("regionresized", region, newSize);
34707         this.layout();
34708     },
34709
34710     onRegionCollapsed : function(region){
34711         this.fireEvent("regioncollapsed", region);
34712     },
34713
34714     onRegionExpanded : function(region){
34715         this.fireEvent("regionexpanded", region);
34716     },
34717
34718     /**
34719      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34720      * performs box-model adjustments.
34721      * @return {Object} The size as an object {width: (the width), height: (the height)}
34722      */
34723     getViewSize : function()
34724     {
34725         var size;
34726         if(this.el.dom != document.body){
34727             size = this.el.getSize();
34728         }else{
34729             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34730         }
34731         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34732         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34733         return size;
34734     },
34735
34736     /**
34737      * Returns the Element this layout is bound to.
34738      * @return {Roo.Element}
34739      */
34740     getEl : function(){
34741         return this.el;
34742     },
34743
34744     /**
34745      * Returns the specified region.
34746      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34747      * @return {Roo.LayoutRegion}
34748      */
34749     getRegion : function(target){
34750         return this.regions[target.toLowerCase()];
34751     },
34752
34753     onWindowResize : function(){
34754         if(this.monitorWindowResize){
34755             this.layout();
34756         }
34757     }
34758 });
34759 /*
34760  * Based on:
34761  * Ext JS Library 1.1.1
34762  * Copyright(c) 2006-2007, Ext JS, LLC.
34763  *
34764  * Originally Released Under LGPL - original licence link has changed is not relivant.
34765  *
34766  * Fork - LGPL
34767  * <script type="text/javascript">
34768  */
34769 /**
34770  * @class Roo.bootstrap.layout.Border
34771  * @extends Roo.bootstrap.layout.Manager
34772  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34773  * please see: examples/bootstrap/nested.html<br><br>
34774  
34775 <b>The container the layout is rendered into can be either the body element or any other element.
34776 If it is not the body element, the container needs to either be an absolute positioned element,
34777 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34778 the container size if it is not the body element.</b>
34779
34780 * @constructor
34781 * Create a new Border
34782 * @param {Object} config Configuration options
34783  */
34784 Roo.bootstrap.layout.Border = function(config){
34785     config = config || {};
34786     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34787     
34788     
34789     
34790     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34791         if(config[region]){
34792             config[region].region = region;
34793             this.addRegion(config[region]);
34794         }
34795     },this);
34796     
34797 };
34798
34799 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34800
34801 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34802     /**
34803      * Creates and adds a new region if it doesn't already exist.
34804      * @param {String} target The target region key (north, south, east, west or center).
34805      * @param {Object} config The regions config object
34806      * @return {BorderLayoutRegion} The new region
34807      */
34808     addRegion : function(config)
34809     {
34810         if(!this.regions[config.region]){
34811             var r = this.factory(config);
34812             this.bindRegion(r);
34813         }
34814         return this.regions[config.region];
34815     },
34816
34817     // private (kinda)
34818     bindRegion : function(r){
34819         this.regions[r.config.region] = r;
34820         
34821         r.on("visibilitychange",    this.layout, this);
34822         r.on("paneladded",          this.layout, this);
34823         r.on("panelremoved",        this.layout, this);
34824         r.on("invalidated",         this.layout, this);
34825         r.on("resized",             this.onRegionResized, this);
34826         r.on("collapsed",           this.onRegionCollapsed, this);
34827         r.on("expanded",            this.onRegionExpanded, this);
34828     },
34829
34830     /**
34831      * Performs a layout update.
34832      */
34833     layout : function()
34834     {
34835         if(this.updating) {
34836             return;
34837         }
34838         
34839         // render all the rebions if they have not been done alreayd?
34840         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34841             if(this.regions[region] && !this.regions[region].bodyEl){
34842                 this.regions[region].onRender(this.el)
34843             }
34844         },this);
34845         
34846         var size = this.getViewSize();
34847         var w = size.width;
34848         var h = size.height;
34849         var centerW = w;
34850         var centerH = h;
34851         var centerY = 0;
34852         var centerX = 0;
34853         //var x = 0, y = 0;
34854
34855         var rs = this.regions;
34856         var north = rs["north"];
34857         var south = rs["south"]; 
34858         var west = rs["west"];
34859         var east = rs["east"];
34860         var center = rs["center"];
34861         //if(this.hideOnLayout){ // not supported anymore
34862             //c.el.setStyle("display", "none");
34863         //}
34864         if(north && north.isVisible()){
34865             var b = north.getBox();
34866             var m = north.getMargins();
34867             b.width = w - (m.left+m.right);
34868             b.x = m.left;
34869             b.y = m.top;
34870             centerY = b.height + b.y + m.bottom;
34871             centerH -= centerY;
34872             north.updateBox(this.safeBox(b));
34873         }
34874         if(south && south.isVisible()){
34875             var b = south.getBox();
34876             var m = south.getMargins();
34877             b.width = w - (m.left+m.right);
34878             b.x = m.left;
34879             var totalHeight = (b.height + m.top + m.bottom);
34880             b.y = h - totalHeight + m.top;
34881             centerH -= totalHeight;
34882             south.updateBox(this.safeBox(b));
34883         }
34884         if(west && west.isVisible()){
34885             var b = west.getBox();
34886             var m = west.getMargins();
34887             b.height = centerH - (m.top+m.bottom);
34888             b.x = m.left;
34889             b.y = centerY + m.top;
34890             var totalWidth = (b.width + m.left + m.right);
34891             centerX += totalWidth;
34892             centerW -= totalWidth;
34893             west.updateBox(this.safeBox(b));
34894         }
34895         if(east && east.isVisible()){
34896             var b = east.getBox();
34897             var m = east.getMargins();
34898             b.height = centerH - (m.top+m.bottom);
34899             var totalWidth = (b.width + m.left + m.right);
34900             b.x = w - totalWidth + m.left;
34901             b.y = centerY + m.top;
34902             centerW -= totalWidth;
34903             east.updateBox(this.safeBox(b));
34904         }
34905         if(center){
34906             var m = center.getMargins();
34907             var centerBox = {
34908                 x: centerX + m.left,
34909                 y: centerY + m.top,
34910                 width: centerW - (m.left+m.right),
34911                 height: centerH - (m.top+m.bottom)
34912             };
34913             //if(this.hideOnLayout){
34914                 //center.el.setStyle("display", "block");
34915             //}
34916             center.updateBox(this.safeBox(centerBox));
34917         }
34918         this.el.repaint();
34919         this.fireEvent("layout", this);
34920     },
34921
34922     // private
34923     safeBox : function(box){
34924         box.width = Math.max(0, box.width);
34925         box.height = Math.max(0, box.height);
34926         return box;
34927     },
34928
34929     /**
34930      * Adds a ContentPanel (or subclass) to this layout.
34931      * @param {String} target The target region key (north, south, east, west or center).
34932      * @param {Roo.ContentPanel} panel The panel to add
34933      * @return {Roo.ContentPanel} The added panel
34934      */
34935     add : function(target, panel){
34936          
34937         target = target.toLowerCase();
34938         return this.regions[target].add(panel);
34939     },
34940
34941     /**
34942      * Remove a ContentPanel (or subclass) to this layout.
34943      * @param {String} target The target region key (north, south, east, west or center).
34944      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34945      * @return {Roo.ContentPanel} The removed panel
34946      */
34947     remove : function(target, panel){
34948         target = target.toLowerCase();
34949         return this.regions[target].remove(panel);
34950     },
34951
34952     /**
34953      * Searches all regions for a panel with the specified id
34954      * @param {String} panelId
34955      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34956      */
34957     findPanel : function(panelId){
34958         var rs = this.regions;
34959         for(var target in rs){
34960             if(typeof rs[target] != "function"){
34961                 var p = rs[target].getPanel(panelId);
34962                 if(p){
34963                     return p;
34964                 }
34965             }
34966         }
34967         return null;
34968     },
34969
34970     /**
34971      * Searches all regions for a panel with the specified id and activates (shows) it.
34972      * @param {String/ContentPanel} panelId The panels id or the panel itself
34973      * @return {Roo.ContentPanel} The shown panel or null
34974      */
34975     showPanel : function(panelId) {
34976       var rs = this.regions;
34977       for(var target in rs){
34978          var r = rs[target];
34979          if(typeof r != "function"){
34980             if(r.hasPanel(panelId)){
34981                return r.showPanel(panelId);
34982             }
34983          }
34984       }
34985       return null;
34986    },
34987
34988    /**
34989      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34990      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34991      */
34992    /*
34993     restoreState : function(provider){
34994         if(!provider){
34995             provider = Roo.state.Manager;
34996         }
34997         var sm = new Roo.LayoutStateManager();
34998         sm.init(this, provider);
34999     },
35000 */
35001  
35002  
35003     /**
35004      * Adds a xtype elements to the layout.
35005      * <pre><code>
35006
35007 layout.addxtype({
35008        xtype : 'ContentPanel',
35009        region: 'west',
35010        items: [ .... ]
35011    }
35012 );
35013
35014 layout.addxtype({
35015         xtype : 'NestedLayoutPanel',
35016         region: 'west',
35017         layout: {
35018            center: { },
35019            west: { }   
35020         },
35021         items : [ ... list of content panels or nested layout panels.. ]
35022    }
35023 );
35024 </code></pre>
35025      * @param {Object} cfg Xtype definition of item to add.
35026      */
35027     addxtype : function(cfg)
35028     {
35029         // basically accepts a pannel...
35030         // can accept a layout region..!?!?
35031         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35032         
35033         
35034         // theory?  children can only be panels??
35035         
35036         //if (!cfg.xtype.match(/Panel$/)) {
35037         //    return false;
35038         //}
35039         var ret = false;
35040         
35041         if (typeof(cfg.region) == 'undefined') {
35042             Roo.log("Failed to add Panel, region was not set");
35043             Roo.log(cfg);
35044             return false;
35045         }
35046         var region = cfg.region;
35047         delete cfg.region;
35048         
35049           
35050         var xitems = [];
35051         if (cfg.items) {
35052             xitems = cfg.items;
35053             delete cfg.items;
35054         }
35055         var nb = false;
35056         
35057         switch(cfg.xtype) 
35058         {
35059             case 'Content':  // ContentPanel (el, cfg)
35060             case 'Scroll':  // ContentPanel (el, cfg)
35061             case 'View': 
35062                 cfg.autoCreate = true;
35063                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35064                 //} else {
35065                 //    var el = this.el.createChild();
35066                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35067                 //}
35068                 
35069                 this.add(region, ret);
35070                 break;
35071             
35072             /*
35073             case 'TreePanel': // our new panel!
35074                 cfg.el = this.el.createChild();
35075                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35076                 this.add(region, ret);
35077                 break;
35078             */
35079             
35080             case 'Nest': 
35081                 // create a new Layout (which is  a Border Layout...
35082                 
35083                 var clayout = cfg.layout;
35084                 clayout.el  = this.el.createChild();
35085                 clayout.items   = clayout.items  || [];
35086                 
35087                 delete cfg.layout;
35088                 
35089                 // replace this exitems with the clayout ones..
35090                 xitems = clayout.items;
35091                  
35092                 // force background off if it's in center...
35093                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35094                     cfg.background = false;
35095                 }
35096                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35097                 
35098                 
35099                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35100                 //console.log('adding nested layout panel '  + cfg.toSource());
35101                 this.add(region, ret);
35102                 nb = {}; /// find first...
35103                 break;
35104             
35105             case 'Grid':
35106                 
35107                 // needs grid and region
35108                 
35109                 //var el = this.getRegion(region).el.createChild();
35110                 /*
35111                  *var el = this.el.createChild();
35112                 // create the grid first...
35113                 cfg.grid.container = el;
35114                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35115                 */
35116                 
35117                 if (region == 'center' && this.active ) {
35118                     cfg.background = false;
35119                 }
35120                 
35121                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35122                 
35123                 this.add(region, ret);
35124                 /*
35125                 if (cfg.background) {
35126                     // render grid on panel activation (if panel background)
35127                     ret.on('activate', function(gp) {
35128                         if (!gp.grid.rendered) {
35129                     //        gp.grid.render(el);
35130                         }
35131                     });
35132                 } else {
35133                   //  cfg.grid.render(el);
35134                 }
35135                 */
35136                 break;
35137            
35138            
35139             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35140                 // it was the old xcomponent building that caused this before.
35141                 // espeically if border is the top element in the tree.
35142                 ret = this;
35143                 break; 
35144                 
35145                     
35146                 
35147                 
35148                 
35149             default:
35150                 /*
35151                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35152                     
35153                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35154                     this.add(region, ret);
35155                 } else {
35156                 */
35157                     Roo.log(cfg);
35158                     throw "Can not add '" + cfg.xtype + "' to Border";
35159                     return null;
35160              
35161                                 
35162              
35163         }
35164         this.beginUpdate();
35165         // add children..
35166         var region = '';
35167         var abn = {};
35168         Roo.each(xitems, function(i)  {
35169             region = nb && i.region ? i.region : false;
35170             
35171             var add = ret.addxtype(i);
35172            
35173             if (region) {
35174                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35175                 if (!i.background) {
35176                     abn[region] = nb[region] ;
35177                 }
35178             }
35179             
35180         });
35181         this.endUpdate();
35182
35183         // make the last non-background panel active..
35184         //if (nb) { Roo.log(abn); }
35185         if (nb) {
35186             
35187             for(var r in abn) {
35188                 region = this.getRegion(r);
35189                 if (region) {
35190                     // tried using nb[r], but it does not work..
35191                      
35192                     region.showPanel(abn[r]);
35193                    
35194                 }
35195             }
35196         }
35197         return ret;
35198         
35199     },
35200     
35201     
35202 // private
35203     factory : function(cfg)
35204     {
35205         
35206         var validRegions = Roo.bootstrap.layout.Border.regions;
35207
35208         var target = cfg.region;
35209         cfg.mgr = this;
35210         
35211         var r = Roo.bootstrap.layout;
35212         Roo.log(target);
35213         switch(target){
35214             case "north":
35215                 return new r.North(cfg);
35216             case "south":
35217                 return new r.South(cfg);
35218             case "east":
35219                 return new r.East(cfg);
35220             case "west":
35221                 return new r.West(cfg);
35222             case "center":
35223                 return new r.Center(cfg);
35224         }
35225         throw 'Layout region "'+target+'" not supported.';
35226     }
35227     
35228     
35229 });
35230  /*
35231  * Based on:
35232  * Ext JS Library 1.1.1
35233  * Copyright(c) 2006-2007, Ext JS, LLC.
35234  *
35235  * Originally Released Under LGPL - original licence link has changed is not relivant.
35236  *
35237  * Fork - LGPL
35238  * <script type="text/javascript">
35239  */
35240  
35241 /**
35242  * @class Roo.bootstrap.layout.Basic
35243  * @extends Roo.util.Observable
35244  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35245  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35246  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35247  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35248  * @cfg {string}   region  the region that it inhabits..
35249  * @cfg {bool}   skipConfig skip config?
35250  * 
35251
35252  */
35253 Roo.bootstrap.layout.Basic = function(config){
35254     
35255     this.mgr = config.mgr;
35256     
35257     this.position = config.region;
35258     
35259     var skipConfig = config.skipConfig;
35260     
35261     this.events = {
35262         /**
35263          * @scope Roo.BasicLayoutRegion
35264          */
35265         
35266         /**
35267          * @event beforeremove
35268          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35269          * @param {Roo.LayoutRegion} this
35270          * @param {Roo.ContentPanel} panel The panel
35271          * @param {Object} e The cancel event object
35272          */
35273         "beforeremove" : true,
35274         /**
35275          * @event invalidated
35276          * Fires when the layout for this region is changed.
35277          * @param {Roo.LayoutRegion} this
35278          */
35279         "invalidated" : true,
35280         /**
35281          * @event visibilitychange
35282          * Fires when this region is shown or hidden 
35283          * @param {Roo.LayoutRegion} this
35284          * @param {Boolean} visibility true or false
35285          */
35286         "visibilitychange" : true,
35287         /**
35288          * @event paneladded
35289          * Fires when a panel is added. 
35290          * @param {Roo.LayoutRegion} this
35291          * @param {Roo.ContentPanel} panel The panel
35292          */
35293         "paneladded" : true,
35294         /**
35295          * @event panelremoved
35296          * Fires when a panel is removed. 
35297          * @param {Roo.LayoutRegion} this
35298          * @param {Roo.ContentPanel} panel The panel
35299          */
35300         "panelremoved" : true,
35301         /**
35302          * @event beforecollapse
35303          * Fires when this region before collapse.
35304          * @param {Roo.LayoutRegion} this
35305          */
35306         "beforecollapse" : true,
35307         /**
35308          * @event collapsed
35309          * Fires when this region is collapsed.
35310          * @param {Roo.LayoutRegion} this
35311          */
35312         "collapsed" : true,
35313         /**
35314          * @event expanded
35315          * Fires when this region is expanded.
35316          * @param {Roo.LayoutRegion} this
35317          */
35318         "expanded" : true,
35319         /**
35320          * @event slideshow
35321          * Fires when this region is slid into view.
35322          * @param {Roo.LayoutRegion} this
35323          */
35324         "slideshow" : true,
35325         /**
35326          * @event slidehide
35327          * Fires when this region slides out of view. 
35328          * @param {Roo.LayoutRegion} this
35329          */
35330         "slidehide" : true,
35331         /**
35332          * @event panelactivated
35333          * Fires when a panel is activated. 
35334          * @param {Roo.LayoutRegion} this
35335          * @param {Roo.ContentPanel} panel The activated panel
35336          */
35337         "panelactivated" : true,
35338         /**
35339          * @event resized
35340          * Fires when the user resizes this region. 
35341          * @param {Roo.LayoutRegion} this
35342          * @param {Number} newSize The new size (width for east/west, height for north/south)
35343          */
35344         "resized" : true
35345     };
35346     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35347     this.panels = new Roo.util.MixedCollection();
35348     this.panels.getKey = this.getPanelId.createDelegate(this);
35349     this.box = null;
35350     this.activePanel = null;
35351     // ensure listeners are added...
35352     
35353     if (config.listeners || config.events) {
35354         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35355             listeners : config.listeners || {},
35356             events : config.events || {}
35357         });
35358     }
35359     
35360     if(skipConfig !== true){
35361         this.applyConfig(config);
35362     }
35363 };
35364
35365 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35366 {
35367     getPanelId : function(p){
35368         return p.getId();
35369     },
35370     
35371     applyConfig : function(config){
35372         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35373         this.config = config;
35374         
35375     },
35376     
35377     /**
35378      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35379      * the width, for horizontal (north, south) the height.
35380      * @param {Number} newSize The new width or height
35381      */
35382     resizeTo : function(newSize){
35383         var el = this.el ? this.el :
35384                  (this.activePanel ? this.activePanel.getEl() : null);
35385         if(el){
35386             switch(this.position){
35387                 case "east":
35388                 case "west":
35389                     el.setWidth(newSize);
35390                     this.fireEvent("resized", this, newSize);
35391                 break;
35392                 case "north":
35393                 case "south":
35394                     el.setHeight(newSize);
35395                     this.fireEvent("resized", this, newSize);
35396                 break;                
35397             }
35398         }
35399     },
35400     
35401     getBox : function(){
35402         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35403     },
35404     
35405     getMargins : function(){
35406         return this.margins;
35407     },
35408     
35409     updateBox : function(box){
35410         this.box = box;
35411         var el = this.activePanel.getEl();
35412         el.dom.style.left = box.x + "px";
35413         el.dom.style.top = box.y + "px";
35414         this.activePanel.setSize(box.width, box.height);
35415     },
35416     
35417     /**
35418      * Returns the container element for this region.
35419      * @return {Roo.Element}
35420      */
35421     getEl : function(){
35422         return this.activePanel;
35423     },
35424     
35425     /**
35426      * Returns true if this region is currently visible.
35427      * @return {Boolean}
35428      */
35429     isVisible : function(){
35430         return this.activePanel ? true : false;
35431     },
35432     
35433     setActivePanel : function(panel){
35434         panel = this.getPanel(panel);
35435         if(this.activePanel && this.activePanel != panel){
35436             this.activePanel.setActiveState(false);
35437             this.activePanel.getEl().setLeftTop(-10000,-10000);
35438         }
35439         this.activePanel = panel;
35440         panel.setActiveState(true);
35441         if(this.box){
35442             panel.setSize(this.box.width, this.box.height);
35443         }
35444         this.fireEvent("panelactivated", this, panel);
35445         this.fireEvent("invalidated");
35446     },
35447     
35448     /**
35449      * Show the specified panel.
35450      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35451      * @return {Roo.ContentPanel} The shown panel or null
35452      */
35453     showPanel : function(panel){
35454         panel = this.getPanel(panel);
35455         if(panel){
35456             this.setActivePanel(panel);
35457         }
35458         return panel;
35459     },
35460     
35461     /**
35462      * Get the active panel for this region.
35463      * @return {Roo.ContentPanel} The active panel or null
35464      */
35465     getActivePanel : function(){
35466         return this.activePanel;
35467     },
35468     
35469     /**
35470      * Add the passed ContentPanel(s)
35471      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35472      * @return {Roo.ContentPanel} The panel added (if only one was added)
35473      */
35474     add : function(panel){
35475         if(arguments.length > 1){
35476             for(var i = 0, len = arguments.length; i < len; i++) {
35477                 this.add(arguments[i]);
35478             }
35479             return null;
35480         }
35481         if(this.hasPanel(panel)){
35482             this.showPanel(panel);
35483             return panel;
35484         }
35485         var el = panel.getEl();
35486         if(el.dom.parentNode != this.mgr.el.dom){
35487             this.mgr.el.dom.appendChild(el.dom);
35488         }
35489         if(panel.setRegion){
35490             panel.setRegion(this);
35491         }
35492         this.panels.add(panel);
35493         el.setStyle("position", "absolute");
35494         if(!panel.background){
35495             this.setActivePanel(panel);
35496             if(this.config.initialSize && this.panels.getCount()==1){
35497                 this.resizeTo(this.config.initialSize);
35498             }
35499         }
35500         this.fireEvent("paneladded", this, panel);
35501         return panel;
35502     },
35503     
35504     /**
35505      * Returns true if the panel is in this region.
35506      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35507      * @return {Boolean}
35508      */
35509     hasPanel : function(panel){
35510         if(typeof panel == "object"){ // must be panel obj
35511             panel = panel.getId();
35512         }
35513         return this.getPanel(panel) ? true : false;
35514     },
35515     
35516     /**
35517      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35518      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35519      * @param {Boolean} preservePanel Overrides the config preservePanel option
35520      * @return {Roo.ContentPanel} The panel that was removed
35521      */
35522     remove : function(panel, preservePanel){
35523         panel = this.getPanel(panel);
35524         if(!panel){
35525             return null;
35526         }
35527         var e = {};
35528         this.fireEvent("beforeremove", this, panel, e);
35529         if(e.cancel === true){
35530             return null;
35531         }
35532         var panelId = panel.getId();
35533         this.panels.removeKey(panelId);
35534         return panel;
35535     },
35536     
35537     /**
35538      * Returns the panel specified or null if it's not in this region.
35539      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35540      * @return {Roo.ContentPanel}
35541      */
35542     getPanel : function(id){
35543         if(typeof id == "object"){ // must be panel obj
35544             return id;
35545         }
35546         return this.panels.get(id);
35547     },
35548     
35549     /**
35550      * Returns this regions position (north/south/east/west/center).
35551      * @return {String} 
35552      */
35553     getPosition: function(){
35554         return this.position;    
35555     }
35556 });/*
35557  * Based on:
35558  * Ext JS Library 1.1.1
35559  * Copyright(c) 2006-2007, Ext JS, LLC.
35560  *
35561  * Originally Released Under LGPL - original licence link has changed is not relivant.
35562  *
35563  * Fork - LGPL
35564  * <script type="text/javascript">
35565  */
35566  
35567 /**
35568  * @class Roo.bootstrap.layout.Region
35569  * @extends Roo.bootstrap.layout.Basic
35570  * This class represents a region in a layout manager.
35571  
35572  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35573  * @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})
35574  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35575  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35576  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35577  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35578  * @cfg {String}    title           The title for the region (overrides panel titles)
35579  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35580  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35581  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35582  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35583  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35584  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35585  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35586  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35587  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35588  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35589
35590  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35591  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35592  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35593  * @cfg {Number}    width           For East/West panels
35594  * @cfg {Number}    height          For North/South panels
35595  * @cfg {Boolean}   split           To show the splitter
35596  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35597  * 
35598  * @cfg {string}   cls             Extra CSS classes to add to region
35599  * 
35600  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35601  * @cfg {string}   region  the region that it inhabits..
35602  *
35603
35604  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35605  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35606
35607  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35608  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35609  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35610  */
35611 Roo.bootstrap.layout.Region = function(config)
35612 {
35613     this.applyConfig(config);
35614
35615     var mgr = config.mgr;
35616     var pos = config.region;
35617     config.skipConfig = true;
35618     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35619     
35620     if (mgr.el) {
35621         this.onRender(mgr.el);   
35622     }
35623      
35624     this.visible = true;
35625     this.collapsed = false;
35626     this.unrendered_panels = [];
35627 };
35628
35629 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35630
35631     position: '', // set by wrapper (eg. north/south etc..)
35632     unrendered_panels : null,  // unrendered panels.
35633     createBody : function(){
35634         /** This region's body element 
35635         * @type Roo.Element */
35636         this.bodyEl = this.el.createChild({
35637                 tag: "div",
35638                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35639         });
35640     },
35641
35642     onRender: function(ctr, pos)
35643     {
35644         var dh = Roo.DomHelper;
35645         /** This region's container element 
35646         * @type Roo.Element */
35647         this.el = dh.append(ctr.dom, {
35648                 tag: "div",
35649                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35650             }, true);
35651         /** This region's title element 
35652         * @type Roo.Element */
35653     
35654         this.titleEl = dh.append(this.el.dom,
35655             {
35656                     tag: "div",
35657                     unselectable: "on",
35658                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35659                     children:[
35660                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35661                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35662                     ]}, true);
35663         
35664         this.titleEl.enableDisplayMode();
35665         /** This region's title text element 
35666         * @type HTMLElement */
35667         this.titleTextEl = this.titleEl.dom.firstChild;
35668         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35669         /*
35670         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35671         this.closeBtn.enableDisplayMode();
35672         this.closeBtn.on("click", this.closeClicked, this);
35673         this.closeBtn.hide();
35674     */
35675         this.createBody(this.config);
35676         if(this.config.hideWhenEmpty){
35677             this.hide();
35678             this.on("paneladded", this.validateVisibility, this);
35679             this.on("panelremoved", this.validateVisibility, this);
35680         }
35681         if(this.autoScroll){
35682             this.bodyEl.setStyle("overflow", "auto");
35683         }else{
35684             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35685         }
35686         //if(c.titlebar !== false){
35687             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35688                 this.titleEl.hide();
35689             }else{
35690                 this.titleEl.show();
35691                 if(this.config.title){
35692                     this.titleTextEl.innerHTML = this.config.title;
35693                 }
35694             }
35695         //}
35696         if(this.config.collapsed){
35697             this.collapse(true);
35698         }
35699         if(this.config.hidden){
35700             this.hide();
35701         }
35702         
35703         if (this.unrendered_panels && this.unrendered_panels.length) {
35704             for (var i =0;i< this.unrendered_panels.length; i++) {
35705                 this.add(this.unrendered_panels[i]);
35706             }
35707             this.unrendered_panels = null;
35708             
35709         }
35710         
35711     },
35712     
35713     applyConfig : function(c)
35714     {
35715         /*
35716          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35717             var dh = Roo.DomHelper;
35718             if(c.titlebar !== false){
35719                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35720                 this.collapseBtn.on("click", this.collapse, this);
35721                 this.collapseBtn.enableDisplayMode();
35722                 /*
35723                 if(c.showPin === true || this.showPin){
35724                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35725                     this.stickBtn.enableDisplayMode();
35726                     this.stickBtn.on("click", this.expand, this);
35727                     this.stickBtn.hide();
35728                 }
35729                 
35730             }
35731             */
35732             /** This region's collapsed element
35733             * @type Roo.Element */
35734             /*
35735              *
35736             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35737                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35738             ]}, true);
35739             
35740             if(c.floatable !== false){
35741                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35742                this.collapsedEl.on("click", this.collapseClick, this);
35743             }
35744
35745             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35746                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35747                    id: "message", unselectable: "on", style:{"float":"left"}});
35748                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35749              }
35750             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35751             this.expandBtn.on("click", this.expand, this);
35752             
35753         }
35754         
35755         if(this.collapseBtn){
35756             this.collapseBtn.setVisible(c.collapsible == true);
35757         }
35758         
35759         this.cmargins = c.cmargins || this.cmargins ||
35760                          (this.position == "west" || this.position == "east" ?
35761                              {top: 0, left: 2, right:2, bottom: 0} :
35762                              {top: 2, left: 0, right:0, bottom: 2});
35763         */
35764         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35765         
35766         
35767         this.bottomTabs = c.tabPosition != "top";
35768         
35769         this.autoScroll = c.autoScroll || false;
35770         
35771         
35772        
35773         
35774         this.duration = c.duration || .30;
35775         this.slideDuration = c.slideDuration || .45;
35776         this.config = c;
35777        
35778     },
35779     /**
35780      * Returns true if this region is currently visible.
35781      * @return {Boolean}
35782      */
35783     isVisible : function(){
35784         return this.visible;
35785     },
35786
35787     /**
35788      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35789      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35790      */
35791     //setCollapsedTitle : function(title){
35792     //    title = title || "&#160;";
35793      //   if(this.collapsedTitleTextEl){
35794       //      this.collapsedTitleTextEl.innerHTML = title;
35795        // }
35796     //},
35797
35798     getBox : function(){
35799         var b;
35800       //  if(!this.collapsed){
35801             b = this.el.getBox(false, true);
35802        // }else{
35803           //  b = this.collapsedEl.getBox(false, true);
35804         //}
35805         return b;
35806     },
35807
35808     getMargins : function(){
35809         return this.margins;
35810         //return this.collapsed ? this.cmargins : this.margins;
35811     },
35812 /*
35813     highlight : function(){
35814         this.el.addClass("x-layout-panel-dragover");
35815     },
35816
35817     unhighlight : function(){
35818         this.el.removeClass("x-layout-panel-dragover");
35819     },
35820 */
35821     updateBox : function(box)
35822     {
35823         if (!this.bodyEl) {
35824             return; // not rendered yet..
35825         }
35826         
35827         this.box = box;
35828         if(!this.collapsed){
35829             this.el.dom.style.left = box.x + "px";
35830             this.el.dom.style.top = box.y + "px";
35831             this.updateBody(box.width, box.height);
35832         }else{
35833             this.collapsedEl.dom.style.left = box.x + "px";
35834             this.collapsedEl.dom.style.top = box.y + "px";
35835             this.collapsedEl.setSize(box.width, box.height);
35836         }
35837         if(this.tabs){
35838             this.tabs.autoSizeTabs();
35839         }
35840     },
35841
35842     updateBody : function(w, h)
35843     {
35844         if(w !== null){
35845             this.el.setWidth(w);
35846             w -= this.el.getBorderWidth("rl");
35847             if(this.config.adjustments){
35848                 w += this.config.adjustments[0];
35849             }
35850         }
35851         if(h !== null && h > 0){
35852             this.el.setHeight(h);
35853             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35854             h -= this.el.getBorderWidth("tb");
35855             if(this.config.adjustments){
35856                 h += this.config.adjustments[1];
35857             }
35858             this.bodyEl.setHeight(h);
35859             if(this.tabs){
35860                 h = this.tabs.syncHeight(h);
35861             }
35862         }
35863         if(this.panelSize){
35864             w = w !== null ? w : this.panelSize.width;
35865             h = h !== null ? h : this.panelSize.height;
35866         }
35867         if(this.activePanel){
35868             var el = this.activePanel.getEl();
35869             w = w !== null ? w : el.getWidth();
35870             h = h !== null ? h : el.getHeight();
35871             this.panelSize = {width: w, height: h};
35872             this.activePanel.setSize(w, h);
35873         }
35874         if(Roo.isIE && this.tabs){
35875             this.tabs.el.repaint();
35876         }
35877     },
35878
35879     /**
35880      * Returns the container element for this region.
35881      * @return {Roo.Element}
35882      */
35883     getEl : function(){
35884         return this.el;
35885     },
35886
35887     /**
35888      * Hides this region.
35889      */
35890     hide : function(){
35891         //if(!this.collapsed){
35892             this.el.dom.style.left = "-2000px";
35893             this.el.hide();
35894         //}else{
35895          //   this.collapsedEl.dom.style.left = "-2000px";
35896          //   this.collapsedEl.hide();
35897        // }
35898         this.visible = false;
35899         this.fireEvent("visibilitychange", this, false);
35900     },
35901
35902     /**
35903      * Shows this region if it was previously hidden.
35904      */
35905     show : function(){
35906         //if(!this.collapsed){
35907             this.el.show();
35908         //}else{
35909         //    this.collapsedEl.show();
35910        // }
35911         this.visible = true;
35912         this.fireEvent("visibilitychange", this, true);
35913     },
35914 /*
35915     closeClicked : function(){
35916         if(this.activePanel){
35917             this.remove(this.activePanel);
35918         }
35919     },
35920
35921     collapseClick : function(e){
35922         if(this.isSlid){
35923            e.stopPropagation();
35924            this.slideIn();
35925         }else{
35926            e.stopPropagation();
35927            this.slideOut();
35928         }
35929     },
35930 */
35931     /**
35932      * Collapses this region.
35933      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35934      */
35935     /*
35936     collapse : function(skipAnim, skipCheck = false){
35937         if(this.collapsed) {
35938             return;
35939         }
35940         
35941         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35942             
35943             this.collapsed = true;
35944             if(this.split){
35945                 this.split.el.hide();
35946             }
35947             if(this.config.animate && skipAnim !== true){
35948                 this.fireEvent("invalidated", this);
35949                 this.animateCollapse();
35950             }else{
35951                 this.el.setLocation(-20000,-20000);
35952                 this.el.hide();
35953                 this.collapsedEl.show();
35954                 this.fireEvent("collapsed", this);
35955                 this.fireEvent("invalidated", this);
35956             }
35957         }
35958         
35959     },
35960 */
35961     animateCollapse : function(){
35962         // overridden
35963     },
35964
35965     /**
35966      * Expands this region if it was previously collapsed.
35967      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35968      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35969      */
35970     /*
35971     expand : function(e, skipAnim){
35972         if(e) {
35973             e.stopPropagation();
35974         }
35975         if(!this.collapsed || this.el.hasActiveFx()) {
35976             return;
35977         }
35978         if(this.isSlid){
35979             this.afterSlideIn();
35980             skipAnim = true;
35981         }
35982         this.collapsed = false;
35983         if(this.config.animate && skipAnim !== true){
35984             this.animateExpand();
35985         }else{
35986             this.el.show();
35987             if(this.split){
35988                 this.split.el.show();
35989             }
35990             this.collapsedEl.setLocation(-2000,-2000);
35991             this.collapsedEl.hide();
35992             this.fireEvent("invalidated", this);
35993             this.fireEvent("expanded", this);
35994         }
35995     },
35996 */
35997     animateExpand : function(){
35998         // overridden
35999     },
36000
36001     initTabs : function()
36002     {
36003         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36004         
36005         var ts = new Roo.bootstrap.panel.Tabs({
36006                 el: this.bodyEl.dom,
36007                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36008                 disableTooltips: this.config.disableTabTips,
36009                 toolbar : this.config.toolbar
36010             });
36011         
36012         if(this.config.hideTabs){
36013             ts.stripWrap.setDisplayed(false);
36014         }
36015         this.tabs = ts;
36016         ts.resizeTabs = this.config.resizeTabs === true;
36017         ts.minTabWidth = this.config.minTabWidth || 40;
36018         ts.maxTabWidth = this.config.maxTabWidth || 250;
36019         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36020         ts.monitorResize = false;
36021         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36022         ts.bodyEl.addClass('roo-layout-tabs-body');
36023         this.panels.each(this.initPanelAsTab, this);
36024     },
36025
36026     initPanelAsTab : function(panel){
36027         var ti = this.tabs.addTab(
36028             panel.getEl().id,
36029             panel.getTitle(),
36030             null,
36031             this.config.closeOnTab && panel.isClosable(),
36032             panel.tpl
36033         );
36034         if(panel.tabTip !== undefined){
36035             ti.setTooltip(panel.tabTip);
36036         }
36037         ti.on("activate", function(){
36038               this.setActivePanel(panel);
36039         }, this);
36040         
36041         if(this.config.closeOnTab){
36042             ti.on("beforeclose", function(t, e){
36043                 e.cancel = true;
36044                 this.remove(panel);
36045             }, this);
36046         }
36047         
36048         panel.tabItem = ti;
36049         
36050         return ti;
36051     },
36052
36053     updatePanelTitle : function(panel, title)
36054     {
36055         if(this.activePanel == panel){
36056             this.updateTitle(title);
36057         }
36058         if(this.tabs){
36059             var ti = this.tabs.getTab(panel.getEl().id);
36060             ti.setText(title);
36061             if(panel.tabTip !== undefined){
36062                 ti.setTooltip(panel.tabTip);
36063             }
36064         }
36065     },
36066
36067     updateTitle : function(title){
36068         if(this.titleTextEl && !this.config.title){
36069             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36070         }
36071     },
36072
36073     setActivePanel : function(panel)
36074     {
36075         panel = this.getPanel(panel);
36076         if(this.activePanel && this.activePanel != panel){
36077             if(this.activePanel.setActiveState(false) === false){
36078                 return;
36079             }
36080         }
36081         this.activePanel = panel;
36082         panel.setActiveState(true);
36083         if(this.panelSize){
36084             panel.setSize(this.panelSize.width, this.panelSize.height);
36085         }
36086         if(this.closeBtn){
36087             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36088         }
36089         this.updateTitle(panel.getTitle());
36090         if(this.tabs){
36091             this.fireEvent("invalidated", this);
36092         }
36093         this.fireEvent("panelactivated", this, panel);
36094     },
36095
36096     /**
36097      * Shows the specified panel.
36098      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36099      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36100      */
36101     showPanel : function(panel)
36102     {
36103         panel = this.getPanel(panel);
36104         if(panel){
36105             if(this.tabs){
36106                 var tab = this.tabs.getTab(panel.getEl().id);
36107                 if(tab.isHidden()){
36108                     this.tabs.unhideTab(tab.id);
36109                 }
36110                 tab.activate();
36111             }else{
36112                 this.setActivePanel(panel);
36113             }
36114         }
36115         return panel;
36116     },
36117
36118     /**
36119      * Get the active panel for this region.
36120      * @return {Roo.ContentPanel} The active panel or null
36121      */
36122     getActivePanel : function(){
36123         return this.activePanel;
36124     },
36125
36126     validateVisibility : function(){
36127         if(this.panels.getCount() < 1){
36128             this.updateTitle("&#160;");
36129             this.closeBtn.hide();
36130             this.hide();
36131         }else{
36132             if(!this.isVisible()){
36133                 this.show();
36134             }
36135         }
36136     },
36137
36138     /**
36139      * Adds the passed ContentPanel(s) to this region.
36140      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36141      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36142      */
36143     add : function(panel)
36144     {
36145         if(arguments.length > 1){
36146             for(var i = 0, len = arguments.length; i < len; i++) {
36147                 this.add(arguments[i]);
36148             }
36149             return null;
36150         }
36151         
36152         // if we have not been rendered yet, then we can not really do much of this..
36153         if (!this.bodyEl) {
36154             this.unrendered_panels.push(panel);
36155             return panel;
36156         }
36157         
36158         
36159         
36160         
36161         if(this.hasPanel(panel)){
36162             this.showPanel(panel);
36163             return panel;
36164         }
36165         panel.setRegion(this);
36166         this.panels.add(panel);
36167        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36168             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36169             // and hide them... ???
36170             this.bodyEl.dom.appendChild(panel.getEl().dom);
36171             if(panel.background !== true){
36172                 this.setActivePanel(panel);
36173             }
36174             this.fireEvent("paneladded", this, panel);
36175             return panel;
36176         }
36177         */
36178         if(!this.tabs){
36179             this.initTabs();
36180         }else{
36181             this.initPanelAsTab(panel);
36182         }
36183         
36184         
36185         if(panel.background !== true){
36186             this.tabs.activate(panel.getEl().id);
36187         }
36188         this.fireEvent("paneladded", this, panel);
36189         return panel;
36190     },
36191
36192     /**
36193      * Hides the tab for the specified panel.
36194      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36195      */
36196     hidePanel : function(panel){
36197         if(this.tabs && (panel = this.getPanel(panel))){
36198             this.tabs.hideTab(panel.getEl().id);
36199         }
36200     },
36201
36202     /**
36203      * Unhides the tab for a previously hidden panel.
36204      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36205      */
36206     unhidePanel : function(panel){
36207         if(this.tabs && (panel = this.getPanel(panel))){
36208             this.tabs.unhideTab(panel.getEl().id);
36209         }
36210     },
36211
36212     clearPanels : function(){
36213         while(this.panels.getCount() > 0){
36214              this.remove(this.panels.first());
36215         }
36216     },
36217
36218     /**
36219      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36220      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36221      * @param {Boolean} preservePanel Overrides the config preservePanel option
36222      * @return {Roo.ContentPanel} The panel that was removed
36223      */
36224     remove : function(panel, preservePanel)
36225     {
36226         panel = this.getPanel(panel);
36227         if(!panel){
36228             return null;
36229         }
36230         var e = {};
36231         this.fireEvent("beforeremove", this, panel, e);
36232         if(e.cancel === true){
36233             return null;
36234         }
36235         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36236         var panelId = panel.getId();
36237         this.panels.removeKey(panelId);
36238         if(preservePanel){
36239             document.body.appendChild(panel.getEl().dom);
36240         }
36241         if(this.tabs){
36242             this.tabs.removeTab(panel.getEl().id);
36243         }else if (!preservePanel){
36244             this.bodyEl.dom.removeChild(panel.getEl().dom);
36245         }
36246         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36247             var p = this.panels.first();
36248             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36249             tempEl.appendChild(p.getEl().dom);
36250             this.bodyEl.update("");
36251             this.bodyEl.dom.appendChild(p.getEl().dom);
36252             tempEl = null;
36253             this.updateTitle(p.getTitle());
36254             this.tabs = null;
36255             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36256             this.setActivePanel(p);
36257         }
36258         panel.setRegion(null);
36259         if(this.activePanel == panel){
36260             this.activePanel = null;
36261         }
36262         if(this.config.autoDestroy !== false && preservePanel !== true){
36263             try{panel.destroy();}catch(e){}
36264         }
36265         this.fireEvent("panelremoved", this, panel);
36266         return panel;
36267     },
36268
36269     /**
36270      * Returns the TabPanel component used by this region
36271      * @return {Roo.TabPanel}
36272      */
36273     getTabs : function(){
36274         return this.tabs;
36275     },
36276
36277     createTool : function(parentEl, className){
36278         var btn = Roo.DomHelper.append(parentEl, {
36279             tag: "div",
36280             cls: "x-layout-tools-button",
36281             children: [ {
36282                 tag: "div",
36283                 cls: "roo-layout-tools-button-inner " + className,
36284                 html: "&#160;"
36285             }]
36286         }, true);
36287         btn.addClassOnOver("roo-layout-tools-button-over");
36288         return btn;
36289     }
36290 });/*
36291  * Based on:
36292  * Ext JS Library 1.1.1
36293  * Copyright(c) 2006-2007, Ext JS, LLC.
36294  *
36295  * Originally Released Under LGPL - original licence link has changed is not relivant.
36296  *
36297  * Fork - LGPL
36298  * <script type="text/javascript">
36299  */
36300  
36301
36302
36303 /**
36304  * @class Roo.SplitLayoutRegion
36305  * @extends Roo.LayoutRegion
36306  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36307  */
36308 Roo.bootstrap.layout.Split = function(config){
36309     this.cursor = config.cursor;
36310     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36311 };
36312
36313 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36314 {
36315     splitTip : "Drag to resize.",
36316     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36317     useSplitTips : false,
36318
36319     applyConfig : function(config){
36320         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36321     },
36322     
36323     onRender : function(ctr,pos) {
36324         
36325         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36326         if(!this.config.split){
36327             return;
36328         }
36329         if(!this.split){
36330             
36331             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36332                             tag: "div",
36333                             id: this.el.id + "-split",
36334                             cls: "roo-layout-split roo-layout-split-"+this.position,
36335                             html: "&#160;"
36336             });
36337             /** The SplitBar for this region 
36338             * @type Roo.SplitBar */
36339             // does not exist yet...
36340             Roo.log([this.position, this.orientation]);
36341             
36342             this.split = new Roo.bootstrap.SplitBar({
36343                 dragElement : splitEl,
36344                 resizingElement: this.el,
36345                 orientation : this.orientation
36346             });
36347             
36348             this.split.on("moved", this.onSplitMove, this);
36349             this.split.useShim = this.config.useShim === true;
36350             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36351             if(this.useSplitTips){
36352                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36353             }
36354             //if(config.collapsible){
36355             //    this.split.el.on("dblclick", this.collapse,  this);
36356             //}
36357         }
36358         if(typeof this.config.minSize != "undefined"){
36359             this.split.minSize = this.config.minSize;
36360         }
36361         if(typeof this.config.maxSize != "undefined"){
36362             this.split.maxSize = this.config.maxSize;
36363         }
36364         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36365             this.hideSplitter();
36366         }
36367         
36368     },
36369
36370     getHMaxSize : function(){
36371          var cmax = this.config.maxSize || 10000;
36372          var center = this.mgr.getRegion("center");
36373          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36374     },
36375
36376     getVMaxSize : function(){
36377          var cmax = this.config.maxSize || 10000;
36378          var center = this.mgr.getRegion("center");
36379          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36380     },
36381
36382     onSplitMove : function(split, newSize){
36383         this.fireEvent("resized", this, newSize);
36384     },
36385     
36386     /** 
36387      * Returns the {@link Roo.SplitBar} for this region.
36388      * @return {Roo.SplitBar}
36389      */
36390     getSplitBar : function(){
36391         return this.split;
36392     },
36393     
36394     hide : function(){
36395         this.hideSplitter();
36396         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36397     },
36398
36399     hideSplitter : function(){
36400         if(this.split){
36401             this.split.el.setLocation(-2000,-2000);
36402             this.split.el.hide();
36403         }
36404     },
36405
36406     show : function(){
36407         if(this.split){
36408             this.split.el.show();
36409         }
36410         Roo.bootstrap.layout.Split.superclass.show.call(this);
36411     },
36412     
36413     beforeSlide: function(){
36414         if(Roo.isGecko){// firefox overflow auto bug workaround
36415             this.bodyEl.clip();
36416             if(this.tabs) {
36417                 this.tabs.bodyEl.clip();
36418             }
36419             if(this.activePanel){
36420                 this.activePanel.getEl().clip();
36421                 
36422                 if(this.activePanel.beforeSlide){
36423                     this.activePanel.beforeSlide();
36424                 }
36425             }
36426         }
36427     },
36428     
36429     afterSlide : function(){
36430         if(Roo.isGecko){// firefox overflow auto bug workaround
36431             this.bodyEl.unclip();
36432             if(this.tabs) {
36433                 this.tabs.bodyEl.unclip();
36434             }
36435             if(this.activePanel){
36436                 this.activePanel.getEl().unclip();
36437                 if(this.activePanel.afterSlide){
36438                     this.activePanel.afterSlide();
36439                 }
36440             }
36441         }
36442     },
36443
36444     initAutoHide : function(){
36445         if(this.autoHide !== false){
36446             if(!this.autoHideHd){
36447                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36448                 this.autoHideHd = {
36449                     "mouseout": function(e){
36450                         if(!e.within(this.el, true)){
36451                             st.delay(500);
36452                         }
36453                     },
36454                     "mouseover" : function(e){
36455                         st.cancel();
36456                     },
36457                     scope : this
36458                 };
36459             }
36460             this.el.on(this.autoHideHd);
36461         }
36462     },
36463
36464     clearAutoHide : function(){
36465         if(this.autoHide !== false){
36466             this.el.un("mouseout", this.autoHideHd.mouseout);
36467             this.el.un("mouseover", this.autoHideHd.mouseover);
36468         }
36469     },
36470
36471     clearMonitor : function(){
36472         Roo.get(document).un("click", this.slideInIf, this);
36473     },
36474
36475     // these names are backwards but not changed for compat
36476     slideOut : function(){
36477         if(this.isSlid || this.el.hasActiveFx()){
36478             return;
36479         }
36480         this.isSlid = true;
36481         if(this.collapseBtn){
36482             this.collapseBtn.hide();
36483         }
36484         this.closeBtnState = this.closeBtn.getStyle('display');
36485         this.closeBtn.hide();
36486         if(this.stickBtn){
36487             this.stickBtn.show();
36488         }
36489         this.el.show();
36490         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36491         this.beforeSlide();
36492         this.el.setStyle("z-index", 10001);
36493         this.el.slideIn(this.getSlideAnchor(), {
36494             callback: function(){
36495                 this.afterSlide();
36496                 this.initAutoHide();
36497                 Roo.get(document).on("click", this.slideInIf, this);
36498                 this.fireEvent("slideshow", this);
36499             },
36500             scope: this,
36501             block: true
36502         });
36503     },
36504
36505     afterSlideIn : function(){
36506         this.clearAutoHide();
36507         this.isSlid = false;
36508         this.clearMonitor();
36509         this.el.setStyle("z-index", "");
36510         if(this.collapseBtn){
36511             this.collapseBtn.show();
36512         }
36513         this.closeBtn.setStyle('display', this.closeBtnState);
36514         if(this.stickBtn){
36515             this.stickBtn.hide();
36516         }
36517         this.fireEvent("slidehide", this);
36518     },
36519
36520     slideIn : function(cb){
36521         if(!this.isSlid || this.el.hasActiveFx()){
36522             Roo.callback(cb);
36523             return;
36524         }
36525         this.isSlid = false;
36526         this.beforeSlide();
36527         this.el.slideOut(this.getSlideAnchor(), {
36528             callback: function(){
36529                 this.el.setLeftTop(-10000, -10000);
36530                 this.afterSlide();
36531                 this.afterSlideIn();
36532                 Roo.callback(cb);
36533             },
36534             scope: this,
36535             block: true
36536         });
36537     },
36538     
36539     slideInIf : function(e){
36540         if(!e.within(this.el)){
36541             this.slideIn();
36542         }
36543     },
36544
36545     animateCollapse : function(){
36546         this.beforeSlide();
36547         this.el.setStyle("z-index", 20000);
36548         var anchor = this.getSlideAnchor();
36549         this.el.slideOut(anchor, {
36550             callback : function(){
36551                 this.el.setStyle("z-index", "");
36552                 this.collapsedEl.slideIn(anchor, {duration:.3});
36553                 this.afterSlide();
36554                 this.el.setLocation(-10000,-10000);
36555                 this.el.hide();
36556                 this.fireEvent("collapsed", this);
36557             },
36558             scope: this,
36559             block: true
36560         });
36561     },
36562
36563     animateExpand : function(){
36564         this.beforeSlide();
36565         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36566         this.el.setStyle("z-index", 20000);
36567         this.collapsedEl.hide({
36568             duration:.1
36569         });
36570         this.el.slideIn(this.getSlideAnchor(), {
36571             callback : function(){
36572                 this.el.setStyle("z-index", "");
36573                 this.afterSlide();
36574                 if(this.split){
36575                     this.split.el.show();
36576                 }
36577                 this.fireEvent("invalidated", this);
36578                 this.fireEvent("expanded", this);
36579             },
36580             scope: this,
36581             block: true
36582         });
36583     },
36584
36585     anchors : {
36586         "west" : "left",
36587         "east" : "right",
36588         "north" : "top",
36589         "south" : "bottom"
36590     },
36591
36592     sanchors : {
36593         "west" : "l",
36594         "east" : "r",
36595         "north" : "t",
36596         "south" : "b"
36597     },
36598
36599     canchors : {
36600         "west" : "tl-tr",
36601         "east" : "tr-tl",
36602         "north" : "tl-bl",
36603         "south" : "bl-tl"
36604     },
36605
36606     getAnchor : function(){
36607         return this.anchors[this.position];
36608     },
36609
36610     getCollapseAnchor : function(){
36611         return this.canchors[this.position];
36612     },
36613
36614     getSlideAnchor : function(){
36615         return this.sanchors[this.position];
36616     },
36617
36618     getAlignAdj : function(){
36619         var cm = this.cmargins;
36620         switch(this.position){
36621             case "west":
36622                 return [0, 0];
36623             break;
36624             case "east":
36625                 return [0, 0];
36626             break;
36627             case "north":
36628                 return [0, 0];
36629             break;
36630             case "south":
36631                 return [0, 0];
36632             break;
36633         }
36634     },
36635
36636     getExpandAdj : function(){
36637         var c = this.collapsedEl, cm = this.cmargins;
36638         switch(this.position){
36639             case "west":
36640                 return [-(cm.right+c.getWidth()+cm.left), 0];
36641             break;
36642             case "east":
36643                 return [cm.right+c.getWidth()+cm.left, 0];
36644             break;
36645             case "north":
36646                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36647             break;
36648             case "south":
36649                 return [0, cm.top+cm.bottom+c.getHeight()];
36650             break;
36651         }
36652     }
36653 });/*
36654  * Based on:
36655  * Ext JS Library 1.1.1
36656  * Copyright(c) 2006-2007, Ext JS, LLC.
36657  *
36658  * Originally Released Under LGPL - original licence link has changed is not relivant.
36659  *
36660  * Fork - LGPL
36661  * <script type="text/javascript">
36662  */
36663 /*
36664  * These classes are private internal classes
36665  */
36666 Roo.bootstrap.layout.Center = function(config){
36667     config.region = "center";
36668     Roo.bootstrap.layout.Region.call(this, config);
36669     this.visible = true;
36670     this.minWidth = config.minWidth || 20;
36671     this.minHeight = config.minHeight || 20;
36672 };
36673
36674 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36675     hide : function(){
36676         // center panel can't be hidden
36677     },
36678     
36679     show : function(){
36680         // center panel can't be hidden
36681     },
36682     
36683     getMinWidth: function(){
36684         return this.minWidth;
36685     },
36686     
36687     getMinHeight: function(){
36688         return this.minHeight;
36689     }
36690 });
36691
36692
36693
36694
36695  
36696
36697
36698
36699
36700
36701 Roo.bootstrap.layout.North = function(config)
36702 {
36703     config.region = 'north';
36704     config.cursor = 'n-resize';
36705     
36706     Roo.bootstrap.layout.Split.call(this, config);
36707     
36708     
36709     if(this.split){
36710         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36711         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36712         this.split.el.addClass("roo-layout-split-v");
36713     }
36714     var size = config.initialSize || config.height;
36715     if(typeof size != "undefined"){
36716         this.el.setHeight(size);
36717     }
36718 };
36719 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36720 {
36721     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36722     
36723     
36724     
36725     getBox : function(){
36726         if(this.collapsed){
36727             return this.collapsedEl.getBox();
36728         }
36729         var box = this.el.getBox();
36730         if(this.split){
36731             box.height += this.split.el.getHeight();
36732         }
36733         return box;
36734     },
36735     
36736     updateBox : function(box){
36737         if(this.split && !this.collapsed){
36738             box.height -= this.split.el.getHeight();
36739             this.split.el.setLeft(box.x);
36740             this.split.el.setTop(box.y+box.height);
36741             this.split.el.setWidth(box.width);
36742         }
36743         if(this.collapsed){
36744             this.updateBody(box.width, null);
36745         }
36746         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36747     }
36748 });
36749
36750
36751
36752
36753
36754 Roo.bootstrap.layout.South = function(config){
36755     config.region = 'south';
36756     config.cursor = 's-resize';
36757     Roo.bootstrap.layout.Split.call(this, config);
36758     if(this.split){
36759         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36760         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36761         this.split.el.addClass("roo-layout-split-v");
36762     }
36763     var size = config.initialSize || config.height;
36764     if(typeof size != "undefined"){
36765         this.el.setHeight(size);
36766     }
36767 };
36768
36769 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36770     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36771     getBox : function(){
36772         if(this.collapsed){
36773             return this.collapsedEl.getBox();
36774         }
36775         var box = this.el.getBox();
36776         if(this.split){
36777             var sh = this.split.el.getHeight();
36778             box.height += sh;
36779             box.y -= sh;
36780         }
36781         return box;
36782     },
36783     
36784     updateBox : function(box){
36785         if(this.split && !this.collapsed){
36786             var sh = this.split.el.getHeight();
36787             box.height -= sh;
36788             box.y += sh;
36789             this.split.el.setLeft(box.x);
36790             this.split.el.setTop(box.y-sh);
36791             this.split.el.setWidth(box.width);
36792         }
36793         if(this.collapsed){
36794             this.updateBody(box.width, null);
36795         }
36796         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36797     }
36798 });
36799
36800 Roo.bootstrap.layout.East = function(config){
36801     config.region = "east";
36802     config.cursor = "e-resize";
36803     Roo.bootstrap.layout.Split.call(this, config);
36804     if(this.split){
36805         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36806         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36807         this.split.el.addClass("roo-layout-split-h");
36808     }
36809     var size = config.initialSize || config.width;
36810     if(typeof size != "undefined"){
36811         this.el.setWidth(size);
36812     }
36813 };
36814 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36815     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36816     getBox : function(){
36817         if(this.collapsed){
36818             return this.collapsedEl.getBox();
36819         }
36820         var box = this.el.getBox();
36821         if(this.split){
36822             var sw = this.split.el.getWidth();
36823             box.width += sw;
36824             box.x -= sw;
36825         }
36826         return box;
36827     },
36828
36829     updateBox : function(box){
36830         if(this.split && !this.collapsed){
36831             var sw = this.split.el.getWidth();
36832             box.width -= sw;
36833             this.split.el.setLeft(box.x);
36834             this.split.el.setTop(box.y);
36835             this.split.el.setHeight(box.height);
36836             box.x += sw;
36837         }
36838         if(this.collapsed){
36839             this.updateBody(null, box.height);
36840         }
36841         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36842     }
36843 });
36844
36845 Roo.bootstrap.layout.West = function(config){
36846     config.region = "west";
36847     config.cursor = "w-resize";
36848     
36849     Roo.bootstrap.layout.Split.call(this, config);
36850     if(this.split){
36851         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36852         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36853         this.split.el.addClass("roo-layout-split-h");
36854     }
36855     
36856 };
36857 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36858     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36859     
36860     onRender: function(ctr, pos)
36861     {
36862         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36863         var size = this.config.initialSize || this.config.width;
36864         if(typeof size != "undefined"){
36865             this.el.setWidth(size);
36866         }
36867     },
36868     
36869     getBox : function(){
36870         if(this.collapsed){
36871             return this.collapsedEl.getBox();
36872         }
36873         var box = this.el.getBox();
36874         if(this.split){
36875             box.width += this.split.el.getWidth();
36876         }
36877         return box;
36878     },
36879     
36880     updateBox : function(box){
36881         if(this.split && !this.collapsed){
36882             var sw = this.split.el.getWidth();
36883             box.width -= sw;
36884             this.split.el.setLeft(box.x+box.width);
36885             this.split.el.setTop(box.y);
36886             this.split.el.setHeight(box.height);
36887         }
36888         if(this.collapsed){
36889             this.updateBody(null, box.height);
36890         }
36891         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36892     }
36893 });
36894 Roo.namespace("Roo.bootstrap.panel");/*
36895  * Based on:
36896  * Ext JS Library 1.1.1
36897  * Copyright(c) 2006-2007, Ext JS, LLC.
36898  *
36899  * Originally Released Under LGPL - original licence link has changed is not relivant.
36900  *
36901  * Fork - LGPL
36902  * <script type="text/javascript">
36903  */
36904 /**
36905  * @class Roo.ContentPanel
36906  * @extends Roo.util.Observable
36907  * A basic ContentPanel element.
36908  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36909  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36910  * @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
36911  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36912  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36913  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36914  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36915  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36916  * @cfg {String} title          The title for this panel
36917  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36918  * @cfg {String} url            Calls {@link #setUrl} with this value
36919  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36920  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36921  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36922  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36923  * @cfg {Boolean} badges render the badges
36924
36925  * @constructor
36926  * Create a new ContentPanel.
36927  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36928  * @param {String/Object} config A string to set only the title or a config object
36929  * @param {String} content (optional) Set the HTML content for this panel
36930  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36931  */
36932 Roo.bootstrap.panel.Content = function( config){
36933     
36934     this.tpl = config.tpl || false;
36935     
36936     var el = config.el;
36937     var content = config.content;
36938
36939     if(config.autoCreate){ // xtype is available if this is called from factory
36940         el = Roo.id();
36941     }
36942     this.el = Roo.get(el);
36943     if(!this.el && config && config.autoCreate){
36944         if(typeof config.autoCreate == "object"){
36945             if(!config.autoCreate.id){
36946                 config.autoCreate.id = config.id||el;
36947             }
36948             this.el = Roo.DomHelper.append(document.body,
36949                         config.autoCreate, true);
36950         }else{
36951             var elcfg =  {   tag: "div",
36952                             cls: "roo-layout-inactive-content",
36953                             id: config.id||el
36954                             };
36955             if (config.html) {
36956                 elcfg.html = config.html;
36957                 
36958             }
36959                         
36960             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36961         }
36962     } 
36963     this.closable = false;
36964     this.loaded = false;
36965     this.active = false;
36966    
36967       
36968     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36969         
36970         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36971         
36972         this.wrapEl = this.el; //this.el.wrap();
36973         var ti = [];
36974         if (config.toolbar.items) {
36975             ti = config.toolbar.items ;
36976             delete config.toolbar.items ;
36977         }
36978         
36979         var nitems = [];
36980         this.toolbar.render(this.wrapEl, 'before');
36981         for(var i =0;i < ti.length;i++) {
36982           //  Roo.log(['add child', items[i]]);
36983             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36984         }
36985         this.toolbar.items = nitems;
36986         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36987         delete config.toolbar;
36988         
36989     }
36990     /*
36991     // xtype created footer. - not sure if will work as we normally have to render first..
36992     if (this.footer && !this.footer.el && this.footer.xtype) {
36993         if (!this.wrapEl) {
36994             this.wrapEl = this.el.wrap();
36995         }
36996     
36997         this.footer.container = this.wrapEl.createChild();
36998          
36999         this.footer = Roo.factory(this.footer, Roo);
37000         
37001     }
37002     */
37003     
37004      if(typeof config == "string"){
37005         this.title = config;
37006     }else{
37007         Roo.apply(this, config);
37008     }
37009     
37010     if(this.resizeEl){
37011         this.resizeEl = Roo.get(this.resizeEl, true);
37012     }else{
37013         this.resizeEl = this.el;
37014     }
37015     // handle view.xtype
37016     
37017  
37018     
37019     
37020     this.addEvents({
37021         /**
37022          * @event activate
37023          * Fires when this panel is activated. 
37024          * @param {Roo.ContentPanel} this
37025          */
37026         "activate" : true,
37027         /**
37028          * @event deactivate
37029          * Fires when this panel is activated. 
37030          * @param {Roo.ContentPanel} this
37031          */
37032         "deactivate" : true,
37033
37034         /**
37035          * @event resize
37036          * Fires when this panel is resized if fitToFrame is true.
37037          * @param {Roo.ContentPanel} this
37038          * @param {Number} width The width after any component adjustments
37039          * @param {Number} height The height after any component adjustments
37040          */
37041         "resize" : true,
37042         
37043          /**
37044          * @event render
37045          * Fires when this tab is created
37046          * @param {Roo.ContentPanel} this
37047          */
37048         "render" : true
37049         
37050         
37051         
37052     });
37053     
37054
37055     
37056     
37057     if(this.autoScroll){
37058         this.resizeEl.setStyle("overflow", "auto");
37059     } else {
37060         // fix randome scrolling
37061         //this.el.on('scroll', function() {
37062         //    Roo.log('fix random scolling');
37063         //    this.scrollTo('top',0); 
37064         //});
37065     }
37066     content = content || this.content;
37067     if(content){
37068         this.setContent(content);
37069     }
37070     if(config && config.url){
37071         this.setUrl(this.url, this.params, this.loadOnce);
37072     }
37073     
37074     
37075     
37076     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37077     
37078     if (this.view && typeof(this.view.xtype) != 'undefined') {
37079         this.view.el = this.el.appendChild(document.createElement("div"));
37080         this.view = Roo.factory(this.view); 
37081         this.view.render  &&  this.view.render(false, '');  
37082     }
37083     
37084     
37085     this.fireEvent('render', this);
37086 };
37087
37088 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37089     
37090     tabTip : '',
37091     
37092     setRegion : function(region){
37093         this.region = region;
37094         this.setActiveClass(region && !this.background);
37095     },
37096     
37097     
37098     setActiveClass: function(state)
37099     {
37100         if(state){
37101            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37102            this.el.setStyle('position','relative');
37103         }else{
37104            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37105            this.el.setStyle('position', 'absolute');
37106         } 
37107     },
37108     
37109     /**
37110      * Returns the toolbar for this Panel if one was configured. 
37111      * @return {Roo.Toolbar} 
37112      */
37113     getToolbar : function(){
37114         return this.toolbar;
37115     },
37116     
37117     setActiveState : function(active)
37118     {
37119         this.active = active;
37120         this.setActiveClass(active);
37121         if(!active){
37122             if(this.fireEvent("deactivate", this) === false){
37123                 return false;
37124             }
37125             return true;
37126         }
37127         this.fireEvent("activate", this);
37128         return true;
37129     },
37130     /**
37131      * Updates this panel's element
37132      * @param {String} content The new content
37133      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37134     */
37135     setContent : function(content, loadScripts){
37136         this.el.update(content, loadScripts);
37137     },
37138
37139     ignoreResize : function(w, h){
37140         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37141             return true;
37142         }else{
37143             this.lastSize = {width: w, height: h};
37144             return false;
37145         }
37146     },
37147     /**
37148      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37149      * @return {Roo.UpdateManager} The UpdateManager
37150      */
37151     getUpdateManager : function(){
37152         return this.el.getUpdateManager();
37153     },
37154      /**
37155      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37156      * @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:
37157 <pre><code>
37158 panel.load({
37159     url: "your-url.php",
37160     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37161     callback: yourFunction,
37162     scope: yourObject, //(optional scope)
37163     discardUrl: false,
37164     nocache: false,
37165     text: "Loading...",
37166     timeout: 30,
37167     scripts: false
37168 });
37169 </code></pre>
37170      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37171      * 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.
37172      * @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}
37173      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37174      * @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.
37175      * @return {Roo.ContentPanel} this
37176      */
37177     load : function(){
37178         var um = this.el.getUpdateManager();
37179         um.update.apply(um, arguments);
37180         return this;
37181     },
37182
37183
37184     /**
37185      * 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.
37186      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37187      * @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)
37188      * @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)
37189      * @return {Roo.UpdateManager} The UpdateManager
37190      */
37191     setUrl : function(url, params, loadOnce){
37192         if(this.refreshDelegate){
37193             this.removeListener("activate", this.refreshDelegate);
37194         }
37195         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37196         this.on("activate", this.refreshDelegate);
37197         return this.el.getUpdateManager();
37198     },
37199     
37200     _handleRefresh : function(url, params, loadOnce){
37201         if(!loadOnce || !this.loaded){
37202             var updater = this.el.getUpdateManager();
37203             updater.update(url, params, this._setLoaded.createDelegate(this));
37204         }
37205     },
37206     
37207     _setLoaded : function(){
37208         this.loaded = true;
37209     }, 
37210     
37211     /**
37212      * Returns this panel's id
37213      * @return {String} 
37214      */
37215     getId : function(){
37216         return this.el.id;
37217     },
37218     
37219     /** 
37220      * Returns this panel's element - used by regiosn to add.
37221      * @return {Roo.Element} 
37222      */
37223     getEl : function(){
37224         return this.wrapEl || this.el;
37225     },
37226     
37227    
37228     
37229     adjustForComponents : function(width, height)
37230     {
37231         //Roo.log('adjustForComponents ');
37232         if(this.resizeEl != this.el){
37233             width -= this.el.getFrameWidth('lr');
37234             height -= this.el.getFrameWidth('tb');
37235         }
37236         if(this.toolbar){
37237             var te = this.toolbar.getEl();
37238             te.setWidth(width);
37239             height -= te.getHeight();
37240         }
37241         if(this.footer){
37242             var te = this.footer.getEl();
37243             te.setWidth(width);
37244             height -= te.getHeight();
37245         }
37246         
37247         
37248         if(this.adjustments){
37249             width += this.adjustments[0];
37250             height += this.adjustments[1];
37251         }
37252         return {"width": width, "height": height};
37253     },
37254     
37255     setSize : function(width, height){
37256         if(this.fitToFrame && !this.ignoreResize(width, height)){
37257             if(this.fitContainer && this.resizeEl != this.el){
37258                 this.el.setSize(width, height);
37259             }
37260             var size = this.adjustForComponents(width, height);
37261             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37262             this.fireEvent('resize', this, size.width, size.height);
37263         }
37264     },
37265     
37266     /**
37267      * Returns this panel's title
37268      * @return {String} 
37269      */
37270     getTitle : function(){
37271         
37272         if (typeof(this.title) != 'object') {
37273             return this.title;
37274         }
37275         
37276         var t = '';
37277         for (var k in this.title) {
37278             if (!this.title.hasOwnProperty(k)) {
37279                 continue;
37280             }
37281             
37282             if (k.indexOf('-') >= 0) {
37283                 var s = k.split('-');
37284                 for (var i = 0; i<s.length; i++) {
37285                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37286                 }
37287             } else {
37288                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37289             }
37290         }
37291         return t;
37292     },
37293     
37294     /**
37295      * Set this panel's title
37296      * @param {String} title
37297      */
37298     setTitle : function(title){
37299         this.title = title;
37300         if(this.region){
37301             this.region.updatePanelTitle(this, title);
37302         }
37303     },
37304     
37305     /**
37306      * Returns true is this panel was configured to be closable
37307      * @return {Boolean} 
37308      */
37309     isClosable : function(){
37310         return this.closable;
37311     },
37312     
37313     beforeSlide : function(){
37314         this.el.clip();
37315         this.resizeEl.clip();
37316     },
37317     
37318     afterSlide : function(){
37319         this.el.unclip();
37320         this.resizeEl.unclip();
37321     },
37322     
37323     /**
37324      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37325      *   Will fail silently if the {@link #setUrl} method has not been called.
37326      *   This does not activate the panel, just updates its content.
37327      */
37328     refresh : function(){
37329         if(this.refreshDelegate){
37330            this.loaded = false;
37331            this.refreshDelegate();
37332         }
37333     },
37334     
37335     /**
37336      * Destroys this panel
37337      */
37338     destroy : function(){
37339         this.el.removeAllListeners();
37340         var tempEl = document.createElement("span");
37341         tempEl.appendChild(this.el.dom);
37342         tempEl.innerHTML = "";
37343         this.el.remove();
37344         this.el = null;
37345     },
37346     
37347     /**
37348      * form - if the content panel contains a form - this is a reference to it.
37349      * @type {Roo.form.Form}
37350      */
37351     form : false,
37352     /**
37353      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37354      *    This contains a reference to it.
37355      * @type {Roo.View}
37356      */
37357     view : false,
37358     
37359       /**
37360      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37361      * <pre><code>
37362
37363 layout.addxtype({
37364        xtype : 'Form',
37365        items: [ .... ]
37366    }
37367 );
37368
37369 </code></pre>
37370      * @param {Object} cfg Xtype definition of item to add.
37371      */
37372     
37373     
37374     getChildContainer: function () {
37375         return this.getEl();
37376     }
37377     
37378     
37379     /*
37380         var  ret = new Roo.factory(cfg);
37381         return ret;
37382         
37383         
37384         // add form..
37385         if (cfg.xtype.match(/^Form$/)) {
37386             
37387             var el;
37388             //if (this.footer) {
37389             //    el = this.footer.container.insertSibling(false, 'before');
37390             //} else {
37391                 el = this.el.createChild();
37392             //}
37393
37394             this.form = new  Roo.form.Form(cfg);
37395             
37396             
37397             if ( this.form.allItems.length) {
37398                 this.form.render(el.dom);
37399             }
37400             return this.form;
37401         }
37402         // should only have one of theses..
37403         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37404             // views.. should not be just added - used named prop 'view''
37405             
37406             cfg.el = this.el.appendChild(document.createElement("div"));
37407             // factory?
37408             
37409             var ret = new Roo.factory(cfg);
37410              
37411              ret.render && ret.render(false, ''); // render blank..
37412             this.view = ret;
37413             return ret;
37414         }
37415         return false;
37416     }
37417     \*/
37418 });
37419  
37420 /**
37421  * @class Roo.bootstrap.panel.Grid
37422  * @extends Roo.bootstrap.panel.Content
37423  * @constructor
37424  * Create a new GridPanel.
37425  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37426  * @param {Object} config A the config object
37427   
37428  */
37429
37430
37431
37432 Roo.bootstrap.panel.Grid = function(config)
37433 {
37434     
37435       
37436     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37437         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37438
37439     config.el = this.wrapper;
37440     //this.el = this.wrapper;
37441     
37442       if (config.container) {
37443         // ctor'ed from a Border/panel.grid
37444         
37445         
37446         this.wrapper.setStyle("overflow", "hidden");
37447         this.wrapper.addClass('roo-grid-container');
37448
37449     }
37450     
37451     
37452     if(config.toolbar){
37453         var tool_el = this.wrapper.createChild();    
37454         this.toolbar = Roo.factory(config.toolbar);
37455         var ti = [];
37456         if (config.toolbar.items) {
37457             ti = config.toolbar.items ;
37458             delete config.toolbar.items ;
37459         }
37460         
37461         var nitems = [];
37462         this.toolbar.render(tool_el);
37463         for(var i =0;i < ti.length;i++) {
37464           //  Roo.log(['add child', items[i]]);
37465             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37466         }
37467         this.toolbar.items = nitems;
37468         
37469         delete config.toolbar;
37470     }
37471     
37472     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37473     config.grid.scrollBody = true;;
37474     config.grid.monitorWindowResize = false; // turn off autosizing
37475     config.grid.autoHeight = false;
37476     config.grid.autoWidth = false;
37477     
37478     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37479     
37480     if (config.background) {
37481         // render grid on panel activation (if panel background)
37482         this.on('activate', function(gp) {
37483             if (!gp.grid.rendered) {
37484                 gp.grid.render(this.wrapper);
37485                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37486             }
37487         });
37488             
37489     } else {
37490         this.grid.render(this.wrapper);
37491         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37492
37493     }
37494     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37495     // ??? needed ??? config.el = this.wrapper;
37496     
37497     
37498     
37499   
37500     // xtype created footer. - not sure if will work as we normally have to render first..
37501     if (this.footer && !this.footer.el && this.footer.xtype) {
37502         
37503         var ctr = this.grid.getView().getFooterPanel(true);
37504         this.footer.dataSource = this.grid.dataSource;
37505         this.footer = Roo.factory(this.footer, Roo);
37506         this.footer.render(ctr);
37507         
37508     }
37509     
37510     
37511     
37512     
37513      
37514 };
37515
37516 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37517     getId : function(){
37518         return this.grid.id;
37519     },
37520     
37521     /**
37522      * Returns the grid for this panel
37523      * @return {Roo.bootstrap.Table} 
37524      */
37525     getGrid : function(){
37526         return this.grid;    
37527     },
37528     
37529     setSize : function(width, height){
37530         if(!this.ignoreResize(width, height)){
37531             var grid = this.grid;
37532             var size = this.adjustForComponents(width, height);
37533             var gridel = grid.getGridEl();
37534             gridel.setSize(size.width, size.height);
37535             /*
37536             var thd = grid.getGridEl().select('thead',true).first();
37537             var tbd = grid.getGridEl().select('tbody', true).first();
37538             if (tbd) {
37539                 tbd.setSize(width, height - thd.getHeight());
37540             }
37541             */
37542             grid.autoSize();
37543         }
37544     },
37545      
37546     
37547     
37548     beforeSlide : function(){
37549         this.grid.getView().scroller.clip();
37550     },
37551     
37552     afterSlide : function(){
37553         this.grid.getView().scroller.unclip();
37554     },
37555     
37556     destroy : function(){
37557         this.grid.destroy();
37558         delete this.grid;
37559         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37560     }
37561 });
37562
37563 /**
37564  * @class Roo.bootstrap.panel.Nest
37565  * @extends Roo.bootstrap.panel.Content
37566  * @constructor
37567  * Create a new Panel, that can contain a layout.Border.
37568  * 
37569  * 
37570  * @param {Roo.BorderLayout} layout The layout for this panel
37571  * @param {String/Object} config A string to set only the title or a config object
37572  */
37573 Roo.bootstrap.panel.Nest = function(config)
37574 {
37575     // construct with only one argument..
37576     /* FIXME - implement nicer consturctors
37577     if (layout.layout) {
37578         config = layout;
37579         layout = config.layout;
37580         delete config.layout;
37581     }
37582     if (layout.xtype && !layout.getEl) {
37583         // then layout needs constructing..
37584         layout = Roo.factory(layout, Roo);
37585     }
37586     */
37587     
37588     config.el =  config.layout.getEl();
37589     
37590     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37591     
37592     config.layout.monitorWindowResize = false; // turn off autosizing
37593     this.layout = config.layout;
37594     this.layout.getEl().addClass("roo-layout-nested-layout");
37595     
37596     
37597     
37598     
37599 };
37600
37601 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37602
37603     setSize : function(width, height){
37604         if(!this.ignoreResize(width, height)){
37605             var size = this.adjustForComponents(width, height);
37606             var el = this.layout.getEl();
37607             if (size.height < 1) {
37608                 el.setWidth(size.width);   
37609             } else {
37610                 el.setSize(size.width, size.height);
37611             }
37612             var touch = el.dom.offsetWidth;
37613             this.layout.layout();
37614             // ie requires a double layout on the first pass
37615             if(Roo.isIE && !this.initialized){
37616                 this.initialized = true;
37617                 this.layout.layout();
37618             }
37619         }
37620     },
37621     
37622     // activate all subpanels if not currently active..
37623     
37624     setActiveState : function(active){
37625         this.active = active;
37626         this.setActiveClass(active);
37627         
37628         if(!active){
37629             this.fireEvent("deactivate", this);
37630             return;
37631         }
37632         
37633         this.fireEvent("activate", this);
37634         // not sure if this should happen before or after..
37635         if (!this.layout) {
37636             return; // should not happen..
37637         }
37638         var reg = false;
37639         for (var r in this.layout.regions) {
37640             reg = this.layout.getRegion(r);
37641             if (reg.getActivePanel()) {
37642                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37643                 reg.setActivePanel(reg.getActivePanel());
37644                 continue;
37645             }
37646             if (!reg.panels.length) {
37647                 continue;
37648             }
37649             reg.showPanel(reg.getPanel(0));
37650         }
37651         
37652         
37653         
37654         
37655     },
37656     
37657     /**
37658      * Returns the nested BorderLayout for this panel
37659      * @return {Roo.BorderLayout} 
37660      */
37661     getLayout : function(){
37662         return this.layout;
37663     },
37664     
37665      /**
37666      * Adds a xtype elements to the layout of the nested panel
37667      * <pre><code>
37668
37669 panel.addxtype({
37670        xtype : 'ContentPanel',
37671        region: 'west',
37672        items: [ .... ]
37673    }
37674 );
37675
37676 panel.addxtype({
37677         xtype : 'NestedLayoutPanel',
37678         region: 'west',
37679         layout: {
37680            center: { },
37681            west: { }   
37682         },
37683         items : [ ... list of content panels or nested layout panels.. ]
37684    }
37685 );
37686 </code></pre>
37687      * @param {Object} cfg Xtype definition of item to add.
37688      */
37689     addxtype : function(cfg) {
37690         return this.layout.addxtype(cfg);
37691     
37692     }
37693 });        /*
37694  * Based on:
37695  * Ext JS Library 1.1.1
37696  * Copyright(c) 2006-2007, Ext JS, LLC.
37697  *
37698  * Originally Released Under LGPL - original licence link has changed is not relivant.
37699  *
37700  * Fork - LGPL
37701  * <script type="text/javascript">
37702  */
37703 /**
37704  * @class Roo.TabPanel
37705  * @extends Roo.util.Observable
37706  * A lightweight tab container.
37707  * <br><br>
37708  * Usage:
37709  * <pre><code>
37710 // basic tabs 1, built from existing content
37711 var tabs = new Roo.TabPanel("tabs1");
37712 tabs.addTab("script", "View Script");
37713 tabs.addTab("markup", "View Markup");
37714 tabs.activate("script");
37715
37716 // more advanced tabs, built from javascript
37717 var jtabs = new Roo.TabPanel("jtabs");
37718 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37719
37720 // set up the UpdateManager
37721 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37722 var updater = tab2.getUpdateManager();
37723 updater.setDefaultUrl("ajax1.htm");
37724 tab2.on('activate', updater.refresh, updater, true);
37725
37726 // Use setUrl for Ajax loading
37727 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37728 tab3.setUrl("ajax2.htm", null, true);
37729
37730 // Disabled tab
37731 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37732 tab4.disable();
37733
37734 jtabs.activate("jtabs-1");
37735  * </code></pre>
37736  * @constructor
37737  * Create a new TabPanel.
37738  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37739  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37740  */
37741 Roo.bootstrap.panel.Tabs = function(config){
37742     /**
37743     * The container element for this TabPanel.
37744     * @type Roo.Element
37745     */
37746     this.el = Roo.get(config.el);
37747     delete config.el;
37748     if(config){
37749         if(typeof config == "boolean"){
37750             this.tabPosition = config ? "bottom" : "top";
37751         }else{
37752             Roo.apply(this, config);
37753         }
37754     }
37755     
37756     if(this.tabPosition == "bottom"){
37757         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37758         this.el.addClass("roo-tabs-bottom");
37759     }
37760     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37761     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37762     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37763     if(Roo.isIE){
37764         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37765     }
37766     if(this.tabPosition != "bottom"){
37767         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37768          * @type Roo.Element
37769          */
37770         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37771         this.el.addClass("roo-tabs-top");
37772     }
37773     this.items = [];
37774
37775     this.bodyEl.setStyle("position", "relative");
37776
37777     this.active = null;
37778     this.activateDelegate = this.activate.createDelegate(this);
37779
37780     this.addEvents({
37781         /**
37782          * @event tabchange
37783          * Fires when the active tab changes
37784          * @param {Roo.TabPanel} this
37785          * @param {Roo.TabPanelItem} activePanel The new active tab
37786          */
37787         "tabchange": true,
37788         /**
37789          * @event beforetabchange
37790          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37791          * @param {Roo.TabPanel} this
37792          * @param {Object} e Set cancel to true on this object to cancel the tab change
37793          * @param {Roo.TabPanelItem} tab The tab being changed to
37794          */
37795         "beforetabchange" : true
37796     });
37797
37798     Roo.EventManager.onWindowResize(this.onResize, this);
37799     this.cpad = this.el.getPadding("lr");
37800     this.hiddenCount = 0;
37801
37802
37803     // toolbar on the tabbar support...
37804     if (this.toolbar) {
37805         alert("no toolbar support yet");
37806         this.toolbar  = false;
37807         /*
37808         var tcfg = this.toolbar;
37809         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37810         this.toolbar = new Roo.Toolbar(tcfg);
37811         if (Roo.isSafari) {
37812             var tbl = tcfg.container.child('table', true);
37813             tbl.setAttribute('width', '100%');
37814         }
37815         */
37816         
37817     }
37818    
37819
37820
37821     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37822 };
37823
37824 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37825     /*
37826      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37827      */
37828     tabPosition : "top",
37829     /*
37830      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37831      */
37832     currentTabWidth : 0,
37833     /*
37834      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37835      */
37836     minTabWidth : 40,
37837     /*
37838      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37839      */
37840     maxTabWidth : 250,
37841     /*
37842      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37843      */
37844     preferredTabWidth : 175,
37845     /*
37846      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37847      */
37848     resizeTabs : false,
37849     /*
37850      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37851      */
37852     monitorResize : true,
37853     /*
37854      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37855      */
37856     toolbar : false,
37857
37858     /**
37859      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37860      * @param {String} id The id of the div to use <b>or create</b>
37861      * @param {String} text The text for the tab
37862      * @param {String} content (optional) Content to put in the TabPanelItem body
37863      * @param {Boolean} closable (optional) True to create a close icon on the tab
37864      * @return {Roo.TabPanelItem} The created TabPanelItem
37865      */
37866     addTab : function(id, text, content, closable, tpl)
37867     {
37868         var item = new Roo.bootstrap.panel.TabItem({
37869             panel: this,
37870             id : id,
37871             text : text,
37872             closable : closable,
37873             tpl : tpl
37874         });
37875         this.addTabItem(item);
37876         if(content){
37877             item.setContent(content);
37878         }
37879         return item;
37880     },
37881
37882     /**
37883      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37884      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37885      * @return {Roo.TabPanelItem}
37886      */
37887     getTab : function(id){
37888         return this.items[id];
37889     },
37890
37891     /**
37892      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37893      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37894      */
37895     hideTab : function(id){
37896         var t = this.items[id];
37897         if(!t.isHidden()){
37898            t.setHidden(true);
37899            this.hiddenCount++;
37900            this.autoSizeTabs();
37901         }
37902     },
37903
37904     /**
37905      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37906      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37907      */
37908     unhideTab : function(id){
37909         var t = this.items[id];
37910         if(t.isHidden()){
37911            t.setHidden(false);
37912            this.hiddenCount--;
37913            this.autoSizeTabs();
37914         }
37915     },
37916
37917     /**
37918      * Adds an existing {@link Roo.TabPanelItem}.
37919      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37920      */
37921     addTabItem : function(item){
37922         this.items[item.id] = item;
37923         this.items.push(item);
37924       //  if(this.resizeTabs){
37925     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37926   //         this.autoSizeTabs();
37927 //        }else{
37928 //            item.autoSize();
37929        // }
37930     },
37931
37932     /**
37933      * Removes a {@link Roo.TabPanelItem}.
37934      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37935      */
37936     removeTab : function(id){
37937         var items = this.items;
37938         var tab = items[id];
37939         if(!tab) { return; }
37940         var index = items.indexOf(tab);
37941         if(this.active == tab && items.length > 1){
37942             var newTab = this.getNextAvailable(index);
37943             if(newTab) {
37944                 newTab.activate();
37945             }
37946         }
37947         this.stripEl.dom.removeChild(tab.pnode.dom);
37948         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37949             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37950         }
37951         items.splice(index, 1);
37952         delete this.items[tab.id];
37953         tab.fireEvent("close", tab);
37954         tab.purgeListeners();
37955         this.autoSizeTabs();
37956     },
37957
37958     getNextAvailable : function(start){
37959         var items = this.items;
37960         var index = start;
37961         // look for a next tab that will slide over to
37962         // replace the one being removed
37963         while(index < items.length){
37964             var item = items[++index];
37965             if(item && !item.isHidden()){
37966                 return item;
37967             }
37968         }
37969         // if one isn't found select the previous tab (on the left)
37970         index = start;
37971         while(index >= 0){
37972             var item = items[--index];
37973             if(item && !item.isHidden()){
37974                 return item;
37975             }
37976         }
37977         return null;
37978     },
37979
37980     /**
37981      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37982      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37983      */
37984     disableTab : function(id){
37985         var tab = this.items[id];
37986         if(tab && this.active != tab){
37987             tab.disable();
37988         }
37989     },
37990
37991     /**
37992      * Enables a {@link Roo.TabPanelItem} that is disabled.
37993      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37994      */
37995     enableTab : function(id){
37996         var tab = this.items[id];
37997         tab.enable();
37998     },
37999
38000     /**
38001      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38002      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38003      * @return {Roo.TabPanelItem} The TabPanelItem.
38004      */
38005     activate : function(id){
38006         var tab = this.items[id];
38007         if(!tab){
38008             return null;
38009         }
38010         if(tab == this.active || tab.disabled){
38011             return tab;
38012         }
38013         var e = {};
38014         this.fireEvent("beforetabchange", this, e, tab);
38015         if(e.cancel !== true && !tab.disabled){
38016             if(this.active){
38017                 this.active.hide();
38018             }
38019             this.active = this.items[id];
38020             this.active.show();
38021             this.fireEvent("tabchange", this, this.active);
38022         }
38023         return tab;
38024     },
38025
38026     /**
38027      * Gets the active {@link Roo.TabPanelItem}.
38028      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38029      */
38030     getActiveTab : function(){
38031         return this.active;
38032     },
38033
38034     /**
38035      * Updates the tab body element to fit the height of the container element
38036      * for overflow scrolling
38037      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38038      */
38039     syncHeight : function(targetHeight){
38040         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38041         var bm = this.bodyEl.getMargins();
38042         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38043         this.bodyEl.setHeight(newHeight);
38044         return newHeight;
38045     },
38046
38047     onResize : function(){
38048         if(this.monitorResize){
38049             this.autoSizeTabs();
38050         }
38051     },
38052
38053     /**
38054      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38055      */
38056     beginUpdate : function(){
38057         this.updating = true;
38058     },
38059
38060     /**
38061      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38062      */
38063     endUpdate : function(){
38064         this.updating = false;
38065         this.autoSizeTabs();
38066     },
38067
38068     /**
38069      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38070      */
38071     autoSizeTabs : function(){
38072         var count = this.items.length;
38073         var vcount = count - this.hiddenCount;
38074         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38075             return;
38076         }
38077         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38078         var availWidth = Math.floor(w / vcount);
38079         var b = this.stripBody;
38080         if(b.getWidth() > w){
38081             var tabs = this.items;
38082             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38083             if(availWidth < this.minTabWidth){
38084                 /*if(!this.sleft){    // incomplete scrolling code
38085                     this.createScrollButtons();
38086                 }
38087                 this.showScroll();
38088                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38089             }
38090         }else{
38091             if(this.currentTabWidth < this.preferredTabWidth){
38092                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38093             }
38094         }
38095     },
38096
38097     /**
38098      * Returns the number of tabs in this TabPanel.
38099      * @return {Number}
38100      */
38101      getCount : function(){
38102          return this.items.length;
38103      },
38104
38105     /**
38106      * Resizes all the tabs to the passed width
38107      * @param {Number} The new width
38108      */
38109     setTabWidth : function(width){
38110         this.currentTabWidth = width;
38111         for(var i = 0, len = this.items.length; i < len; i++) {
38112                 if(!this.items[i].isHidden()) {
38113                 this.items[i].setWidth(width);
38114             }
38115         }
38116     },
38117
38118     /**
38119      * Destroys this TabPanel
38120      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38121      */
38122     destroy : function(removeEl){
38123         Roo.EventManager.removeResizeListener(this.onResize, this);
38124         for(var i = 0, len = this.items.length; i < len; i++){
38125             this.items[i].purgeListeners();
38126         }
38127         if(removeEl === true){
38128             this.el.update("");
38129             this.el.remove();
38130         }
38131     },
38132     
38133     createStrip : function(container)
38134     {
38135         var strip = document.createElement("nav");
38136         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38137         container.appendChild(strip);
38138         return strip;
38139     },
38140     
38141     createStripList : function(strip)
38142     {
38143         // div wrapper for retard IE
38144         // returns the "tr" element.
38145         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38146         //'<div class="x-tabs-strip-wrap">'+
38147           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38148           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38149         return strip.firstChild; //.firstChild.firstChild.firstChild;
38150     },
38151     createBody : function(container)
38152     {
38153         var body = document.createElement("div");
38154         Roo.id(body, "tab-body");
38155         //Roo.fly(body).addClass("x-tabs-body");
38156         Roo.fly(body).addClass("tab-content");
38157         container.appendChild(body);
38158         return body;
38159     },
38160     createItemBody :function(bodyEl, id){
38161         var body = Roo.getDom(id);
38162         if(!body){
38163             body = document.createElement("div");
38164             body.id = id;
38165         }
38166         //Roo.fly(body).addClass("x-tabs-item-body");
38167         Roo.fly(body).addClass("tab-pane");
38168          bodyEl.insertBefore(body, bodyEl.firstChild);
38169         return body;
38170     },
38171     /** @private */
38172     createStripElements :  function(stripEl, text, closable, tpl)
38173     {
38174         var td = document.createElement("li"); // was td..
38175         
38176         
38177         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38178         
38179         
38180         stripEl.appendChild(td);
38181         /*if(closable){
38182             td.className = "x-tabs-closable";
38183             if(!this.closeTpl){
38184                 this.closeTpl = new Roo.Template(
38185                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38186                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38187                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38188                 );
38189             }
38190             var el = this.closeTpl.overwrite(td, {"text": text});
38191             var close = el.getElementsByTagName("div")[0];
38192             var inner = el.getElementsByTagName("em")[0];
38193             return {"el": el, "close": close, "inner": inner};
38194         } else {
38195         */
38196         // not sure what this is..
38197 //            if(!this.tabTpl){
38198                 //this.tabTpl = new Roo.Template(
38199                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38200                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38201                 //);
38202 //                this.tabTpl = new Roo.Template(
38203 //                   '<a href="#">' +
38204 //                   '<span unselectable="on"' +
38205 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38206 //                            ' >{text}</span></a>'
38207 //                );
38208 //                
38209 //            }
38210
38211
38212             var template = tpl || this.tabTpl || false;
38213             
38214             if(!template){
38215                 
38216                 template = new Roo.Template(
38217                    '<a href="#">' +
38218                    '<span unselectable="on"' +
38219                             (this.disableTooltips ? '' : ' title="{text}"') +
38220                             ' >{text}</span></a>'
38221                 );
38222             }
38223             
38224             switch (typeof(template)) {
38225                 case 'object' :
38226                     break;
38227                 case 'string' :
38228                     template = new Roo.Template(template);
38229                     break;
38230                 default :
38231                     break;
38232             }
38233             
38234             var el = template.overwrite(td, {"text": text});
38235             
38236             var inner = el.getElementsByTagName("span")[0];
38237             
38238             return {"el": el, "inner": inner};
38239             
38240     }
38241         
38242     
38243 });
38244
38245 /**
38246  * @class Roo.TabPanelItem
38247  * @extends Roo.util.Observable
38248  * Represents an individual item (tab plus body) in a TabPanel.
38249  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38250  * @param {String} id The id of this TabPanelItem
38251  * @param {String} text The text for the tab of this TabPanelItem
38252  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38253  */
38254 Roo.bootstrap.panel.TabItem = function(config){
38255     /**
38256      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38257      * @type Roo.TabPanel
38258      */
38259     this.tabPanel = config.panel;
38260     /**
38261      * The id for this TabPanelItem
38262      * @type String
38263      */
38264     this.id = config.id;
38265     /** @private */
38266     this.disabled = false;
38267     /** @private */
38268     this.text = config.text;
38269     /** @private */
38270     this.loaded = false;
38271     this.closable = config.closable;
38272
38273     /**
38274      * The body element for this TabPanelItem.
38275      * @type Roo.Element
38276      */
38277     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38278     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38279     this.bodyEl.setStyle("display", "block");
38280     this.bodyEl.setStyle("zoom", "1");
38281     //this.hideAction();
38282
38283     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38284     /** @private */
38285     this.el = Roo.get(els.el);
38286     this.inner = Roo.get(els.inner, true);
38287     this.textEl = Roo.get(this.el.dom.firstChild, true);
38288     this.pnode = Roo.get(els.el.parentNode, true);
38289 //    this.el.on("mousedown", this.onTabMouseDown, this);
38290     this.el.on("click", this.onTabClick, this);
38291     /** @private */
38292     if(config.closable){
38293         var c = Roo.get(els.close, true);
38294         c.dom.title = this.closeText;
38295         c.addClassOnOver("close-over");
38296         c.on("click", this.closeClick, this);
38297      }
38298
38299     this.addEvents({
38300          /**
38301          * @event activate
38302          * Fires when this tab becomes the active tab.
38303          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38304          * @param {Roo.TabPanelItem} this
38305          */
38306         "activate": true,
38307         /**
38308          * @event beforeclose
38309          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38310          * @param {Roo.TabPanelItem} this
38311          * @param {Object} e Set cancel to true on this object to cancel the close.
38312          */
38313         "beforeclose": true,
38314         /**
38315          * @event close
38316          * Fires when this tab is closed.
38317          * @param {Roo.TabPanelItem} this
38318          */
38319          "close": true,
38320         /**
38321          * @event deactivate
38322          * Fires when this tab is no longer the active tab.
38323          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38324          * @param {Roo.TabPanelItem} this
38325          */
38326          "deactivate" : true
38327     });
38328     this.hidden = false;
38329
38330     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38331 };
38332
38333 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38334            {
38335     purgeListeners : function(){
38336        Roo.util.Observable.prototype.purgeListeners.call(this);
38337        this.el.removeAllListeners();
38338     },
38339     /**
38340      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38341      */
38342     show : function(){
38343         this.pnode.addClass("active");
38344         this.showAction();
38345         if(Roo.isOpera){
38346             this.tabPanel.stripWrap.repaint();
38347         }
38348         this.fireEvent("activate", this.tabPanel, this);
38349     },
38350
38351     /**
38352      * Returns true if this tab is the active tab.
38353      * @return {Boolean}
38354      */
38355     isActive : function(){
38356         return this.tabPanel.getActiveTab() == this;
38357     },
38358
38359     /**
38360      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38361      */
38362     hide : function(){
38363         this.pnode.removeClass("active");
38364         this.hideAction();
38365         this.fireEvent("deactivate", this.tabPanel, this);
38366     },
38367
38368     hideAction : function(){
38369         this.bodyEl.hide();
38370         this.bodyEl.setStyle("position", "absolute");
38371         this.bodyEl.setLeft("-20000px");
38372         this.bodyEl.setTop("-20000px");
38373     },
38374
38375     showAction : function(){
38376         this.bodyEl.setStyle("position", "relative");
38377         this.bodyEl.setTop("");
38378         this.bodyEl.setLeft("");
38379         this.bodyEl.show();
38380     },
38381
38382     /**
38383      * Set the tooltip for the tab.
38384      * @param {String} tooltip The tab's tooltip
38385      */
38386     setTooltip : function(text){
38387         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38388             this.textEl.dom.qtip = text;
38389             this.textEl.dom.removeAttribute('title');
38390         }else{
38391             this.textEl.dom.title = text;
38392         }
38393     },
38394
38395     onTabClick : function(e){
38396         e.preventDefault();
38397         this.tabPanel.activate(this.id);
38398     },
38399
38400     onTabMouseDown : function(e){
38401         e.preventDefault();
38402         this.tabPanel.activate(this.id);
38403     },
38404 /*
38405     getWidth : function(){
38406         return this.inner.getWidth();
38407     },
38408
38409     setWidth : function(width){
38410         var iwidth = width - this.pnode.getPadding("lr");
38411         this.inner.setWidth(iwidth);
38412         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38413         this.pnode.setWidth(width);
38414     },
38415 */
38416     /**
38417      * Show or hide the tab
38418      * @param {Boolean} hidden True to hide or false to show.
38419      */
38420     setHidden : function(hidden){
38421         this.hidden = hidden;
38422         this.pnode.setStyle("display", hidden ? "none" : "");
38423     },
38424
38425     /**
38426      * Returns true if this tab is "hidden"
38427      * @return {Boolean}
38428      */
38429     isHidden : function(){
38430         return this.hidden;
38431     },
38432
38433     /**
38434      * Returns the text for this tab
38435      * @return {String}
38436      */
38437     getText : function(){
38438         return this.text;
38439     },
38440     /*
38441     autoSize : function(){
38442         //this.el.beginMeasure();
38443         this.textEl.setWidth(1);
38444         /*
38445          *  #2804 [new] Tabs in Roojs
38446          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38447          */
38448         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38449         //this.el.endMeasure();
38450     //},
38451
38452     /**
38453      * Sets the text for the tab (Note: this also sets the tooltip text)
38454      * @param {String} text The tab's text and tooltip
38455      */
38456     setText : function(text){
38457         this.text = text;
38458         this.textEl.update(text);
38459         this.setTooltip(text);
38460         //if(!this.tabPanel.resizeTabs){
38461         //    this.autoSize();
38462         //}
38463     },
38464     /**
38465      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38466      */
38467     activate : function(){
38468         this.tabPanel.activate(this.id);
38469     },
38470
38471     /**
38472      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38473      */
38474     disable : function(){
38475         if(this.tabPanel.active != this){
38476             this.disabled = true;
38477             this.pnode.addClass("disabled");
38478         }
38479     },
38480
38481     /**
38482      * Enables this TabPanelItem if it was previously disabled.
38483      */
38484     enable : function(){
38485         this.disabled = false;
38486         this.pnode.removeClass("disabled");
38487     },
38488
38489     /**
38490      * Sets the content for this TabPanelItem.
38491      * @param {String} content The content
38492      * @param {Boolean} loadScripts true to look for and load scripts
38493      */
38494     setContent : function(content, loadScripts){
38495         this.bodyEl.update(content, loadScripts);
38496     },
38497
38498     /**
38499      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38500      * @return {Roo.UpdateManager} The UpdateManager
38501      */
38502     getUpdateManager : function(){
38503         return this.bodyEl.getUpdateManager();
38504     },
38505
38506     /**
38507      * Set a URL to be used to load the content for this TabPanelItem.
38508      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38509      * @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)
38510      * @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)
38511      * @return {Roo.UpdateManager} The UpdateManager
38512      */
38513     setUrl : function(url, params, loadOnce){
38514         if(this.refreshDelegate){
38515             this.un('activate', this.refreshDelegate);
38516         }
38517         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38518         this.on("activate", this.refreshDelegate);
38519         return this.bodyEl.getUpdateManager();
38520     },
38521
38522     /** @private */
38523     _handleRefresh : function(url, params, loadOnce){
38524         if(!loadOnce || !this.loaded){
38525             var updater = this.bodyEl.getUpdateManager();
38526             updater.update(url, params, this._setLoaded.createDelegate(this));
38527         }
38528     },
38529
38530     /**
38531      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38532      *   Will fail silently if the setUrl method has not been called.
38533      *   This does not activate the panel, just updates its content.
38534      */
38535     refresh : function(){
38536         if(this.refreshDelegate){
38537            this.loaded = false;
38538            this.refreshDelegate();
38539         }
38540     },
38541
38542     /** @private */
38543     _setLoaded : function(){
38544         this.loaded = true;
38545     },
38546
38547     /** @private */
38548     closeClick : function(e){
38549         var o = {};
38550         e.stopEvent();
38551         this.fireEvent("beforeclose", this, o);
38552         if(o.cancel !== true){
38553             this.tabPanel.removeTab(this.id);
38554         }
38555     },
38556     /**
38557      * The text displayed in the tooltip for the close icon.
38558      * @type String
38559      */
38560     closeText : "Close this tab"
38561 });
38562 /**
38563 *    This script refer to:
38564 *    Title: International Telephone Input
38565 *    Author: Jack O'Connor
38566 *    Code version:  v12.1.12
38567 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38568 **/
38569
38570 Roo.bootstrap.PhoneInputData = function() {
38571     var d = [
38572       [
38573         "Afghanistan (‫افغانستان‬‎)",
38574         "af",
38575         "93"
38576       ],
38577       [
38578         "Albania (Shqipëri)",
38579         "al",
38580         "355"
38581       ],
38582       [
38583         "Algeria (‫الجزائر‬‎)",
38584         "dz",
38585         "213"
38586       ],
38587       [
38588         "American Samoa",
38589         "as",
38590         "1684"
38591       ],
38592       [
38593         "Andorra",
38594         "ad",
38595         "376"
38596       ],
38597       [
38598         "Angola",
38599         "ao",
38600         "244"
38601       ],
38602       [
38603         "Anguilla",
38604         "ai",
38605         "1264"
38606       ],
38607       [
38608         "Antigua and Barbuda",
38609         "ag",
38610         "1268"
38611       ],
38612       [
38613         "Argentina",
38614         "ar",
38615         "54"
38616       ],
38617       [
38618         "Armenia (Հայաստան)",
38619         "am",
38620         "374"
38621       ],
38622       [
38623         "Aruba",
38624         "aw",
38625         "297"
38626       ],
38627       [
38628         "Australia",
38629         "au",
38630         "61",
38631         0
38632       ],
38633       [
38634         "Austria (Österreich)",
38635         "at",
38636         "43"
38637       ],
38638       [
38639         "Azerbaijan (Azərbaycan)",
38640         "az",
38641         "994"
38642       ],
38643       [
38644         "Bahamas",
38645         "bs",
38646         "1242"
38647       ],
38648       [
38649         "Bahrain (‫البحرين‬‎)",
38650         "bh",
38651         "973"
38652       ],
38653       [
38654         "Bangladesh (বাংলাদেশ)",
38655         "bd",
38656         "880"
38657       ],
38658       [
38659         "Barbados",
38660         "bb",
38661         "1246"
38662       ],
38663       [
38664         "Belarus (Беларусь)",
38665         "by",
38666         "375"
38667       ],
38668       [
38669         "Belgium (België)",
38670         "be",
38671         "32"
38672       ],
38673       [
38674         "Belize",
38675         "bz",
38676         "501"
38677       ],
38678       [
38679         "Benin (Bénin)",
38680         "bj",
38681         "229"
38682       ],
38683       [
38684         "Bermuda",
38685         "bm",
38686         "1441"
38687       ],
38688       [
38689         "Bhutan (འབྲུག)",
38690         "bt",
38691         "975"
38692       ],
38693       [
38694         "Bolivia",
38695         "bo",
38696         "591"
38697       ],
38698       [
38699         "Bosnia and Herzegovina (Босна и Херцеговина)",
38700         "ba",
38701         "387"
38702       ],
38703       [
38704         "Botswana",
38705         "bw",
38706         "267"
38707       ],
38708       [
38709         "Brazil (Brasil)",
38710         "br",
38711         "55"
38712       ],
38713       [
38714         "British Indian Ocean Territory",
38715         "io",
38716         "246"
38717       ],
38718       [
38719         "British Virgin Islands",
38720         "vg",
38721         "1284"
38722       ],
38723       [
38724         "Brunei",
38725         "bn",
38726         "673"
38727       ],
38728       [
38729         "Bulgaria (България)",
38730         "bg",
38731         "359"
38732       ],
38733       [
38734         "Burkina Faso",
38735         "bf",
38736         "226"
38737       ],
38738       [
38739         "Burundi (Uburundi)",
38740         "bi",
38741         "257"
38742       ],
38743       [
38744         "Cambodia (កម្ពុជា)",
38745         "kh",
38746         "855"
38747       ],
38748       [
38749         "Cameroon (Cameroun)",
38750         "cm",
38751         "237"
38752       ],
38753       [
38754         "Canada",
38755         "ca",
38756         "1",
38757         1,
38758         ["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"]
38759       ],
38760       [
38761         "Cape Verde (Kabu Verdi)",
38762         "cv",
38763         "238"
38764       ],
38765       [
38766         "Caribbean Netherlands",
38767         "bq",
38768         "599",
38769         1
38770       ],
38771       [
38772         "Cayman Islands",
38773         "ky",
38774         "1345"
38775       ],
38776       [
38777         "Central African Republic (République centrafricaine)",
38778         "cf",
38779         "236"
38780       ],
38781       [
38782         "Chad (Tchad)",
38783         "td",
38784         "235"
38785       ],
38786       [
38787         "Chile",
38788         "cl",
38789         "56"
38790       ],
38791       [
38792         "China (中国)",
38793         "cn",
38794         "86"
38795       ],
38796       [
38797         "Christmas Island",
38798         "cx",
38799         "61",
38800         2
38801       ],
38802       [
38803         "Cocos (Keeling) Islands",
38804         "cc",
38805         "61",
38806         1
38807       ],
38808       [
38809         "Colombia",
38810         "co",
38811         "57"
38812       ],
38813       [
38814         "Comoros (‫جزر القمر‬‎)",
38815         "km",
38816         "269"
38817       ],
38818       [
38819         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38820         "cd",
38821         "243"
38822       ],
38823       [
38824         "Congo (Republic) (Congo-Brazzaville)",
38825         "cg",
38826         "242"
38827       ],
38828       [
38829         "Cook Islands",
38830         "ck",
38831         "682"
38832       ],
38833       [
38834         "Costa Rica",
38835         "cr",
38836         "506"
38837       ],
38838       [
38839         "Côte d’Ivoire",
38840         "ci",
38841         "225"
38842       ],
38843       [
38844         "Croatia (Hrvatska)",
38845         "hr",
38846         "385"
38847       ],
38848       [
38849         "Cuba",
38850         "cu",
38851         "53"
38852       ],
38853       [
38854         "Curaçao",
38855         "cw",
38856         "599",
38857         0
38858       ],
38859       [
38860         "Cyprus (Κύπρος)",
38861         "cy",
38862         "357"
38863       ],
38864       [
38865         "Czech Republic (Česká republika)",
38866         "cz",
38867         "420"
38868       ],
38869       [
38870         "Denmark (Danmark)",
38871         "dk",
38872         "45"
38873       ],
38874       [
38875         "Djibouti",
38876         "dj",
38877         "253"
38878       ],
38879       [
38880         "Dominica",
38881         "dm",
38882         "1767"
38883       ],
38884       [
38885         "Dominican Republic (República Dominicana)",
38886         "do",
38887         "1",
38888         2,
38889         ["809", "829", "849"]
38890       ],
38891       [
38892         "Ecuador",
38893         "ec",
38894         "593"
38895       ],
38896       [
38897         "Egypt (‫مصر‬‎)",
38898         "eg",
38899         "20"
38900       ],
38901       [
38902         "El Salvador",
38903         "sv",
38904         "503"
38905       ],
38906       [
38907         "Equatorial Guinea (Guinea Ecuatorial)",
38908         "gq",
38909         "240"
38910       ],
38911       [
38912         "Eritrea",
38913         "er",
38914         "291"
38915       ],
38916       [
38917         "Estonia (Eesti)",
38918         "ee",
38919         "372"
38920       ],
38921       [
38922         "Ethiopia",
38923         "et",
38924         "251"
38925       ],
38926       [
38927         "Falkland Islands (Islas Malvinas)",
38928         "fk",
38929         "500"
38930       ],
38931       [
38932         "Faroe Islands (Føroyar)",
38933         "fo",
38934         "298"
38935       ],
38936       [
38937         "Fiji",
38938         "fj",
38939         "679"
38940       ],
38941       [
38942         "Finland (Suomi)",
38943         "fi",
38944         "358",
38945         0
38946       ],
38947       [
38948         "France",
38949         "fr",
38950         "33"
38951       ],
38952       [
38953         "French Guiana (Guyane française)",
38954         "gf",
38955         "594"
38956       ],
38957       [
38958         "French Polynesia (Polynésie française)",
38959         "pf",
38960         "689"
38961       ],
38962       [
38963         "Gabon",
38964         "ga",
38965         "241"
38966       ],
38967       [
38968         "Gambia",
38969         "gm",
38970         "220"
38971       ],
38972       [
38973         "Georgia (საქართველო)",
38974         "ge",
38975         "995"
38976       ],
38977       [
38978         "Germany (Deutschland)",
38979         "de",
38980         "49"
38981       ],
38982       [
38983         "Ghana (Gaana)",
38984         "gh",
38985         "233"
38986       ],
38987       [
38988         "Gibraltar",
38989         "gi",
38990         "350"
38991       ],
38992       [
38993         "Greece (Ελλάδα)",
38994         "gr",
38995         "30"
38996       ],
38997       [
38998         "Greenland (Kalaallit Nunaat)",
38999         "gl",
39000         "299"
39001       ],
39002       [
39003         "Grenada",
39004         "gd",
39005         "1473"
39006       ],
39007       [
39008         "Guadeloupe",
39009         "gp",
39010         "590",
39011         0
39012       ],
39013       [
39014         "Guam",
39015         "gu",
39016         "1671"
39017       ],
39018       [
39019         "Guatemala",
39020         "gt",
39021         "502"
39022       ],
39023       [
39024         "Guernsey",
39025         "gg",
39026         "44",
39027         1
39028       ],
39029       [
39030         "Guinea (Guinée)",
39031         "gn",
39032         "224"
39033       ],
39034       [
39035         "Guinea-Bissau (Guiné Bissau)",
39036         "gw",
39037         "245"
39038       ],
39039       [
39040         "Guyana",
39041         "gy",
39042         "592"
39043       ],
39044       [
39045         "Haiti",
39046         "ht",
39047         "509"
39048       ],
39049       [
39050         "Honduras",
39051         "hn",
39052         "504"
39053       ],
39054       [
39055         "Hong Kong (香港)",
39056         "hk",
39057         "852"
39058       ],
39059       [
39060         "Hungary (Magyarország)",
39061         "hu",
39062         "36"
39063       ],
39064       [
39065         "Iceland (Ísland)",
39066         "is",
39067         "354"
39068       ],
39069       [
39070         "India (भारत)",
39071         "in",
39072         "91"
39073       ],
39074       [
39075         "Indonesia",
39076         "id",
39077         "62"
39078       ],
39079       [
39080         "Iran (‫ایران‬‎)",
39081         "ir",
39082         "98"
39083       ],
39084       [
39085         "Iraq (‫العراق‬‎)",
39086         "iq",
39087         "964"
39088       ],
39089       [
39090         "Ireland",
39091         "ie",
39092         "353"
39093       ],
39094       [
39095         "Isle of Man",
39096         "im",
39097         "44",
39098         2
39099       ],
39100       [
39101         "Israel (‫ישראל‬‎)",
39102         "il",
39103         "972"
39104       ],
39105       [
39106         "Italy (Italia)",
39107         "it",
39108         "39",
39109         0
39110       ],
39111       [
39112         "Jamaica",
39113         "jm",
39114         "1876"
39115       ],
39116       [
39117         "Japan (日本)",
39118         "jp",
39119         "81"
39120       ],
39121       [
39122         "Jersey",
39123         "je",
39124         "44",
39125         3
39126       ],
39127       [
39128         "Jordan (‫الأردن‬‎)",
39129         "jo",
39130         "962"
39131       ],
39132       [
39133         "Kazakhstan (Казахстан)",
39134         "kz",
39135         "7",
39136         1
39137       ],
39138       [
39139         "Kenya",
39140         "ke",
39141         "254"
39142       ],
39143       [
39144         "Kiribati",
39145         "ki",
39146         "686"
39147       ],
39148       [
39149         "Kosovo",
39150         "xk",
39151         "383"
39152       ],
39153       [
39154         "Kuwait (‫الكويت‬‎)",
39155         "kw",
39156         "965"
39157       ],
39158       [
39159         "Kyrgyzstan (Кыргызстан)",
39160         "kg",
39161         "996"
39162       ],
39163       [
39164         "Laos (ລາວ)",
39165         "la",
39166         "856"
39167       ],
39168       [
39169         "Latvia (Latvija)",
39170         "lv",
39171         "371"
39172       ],
39173       [
39174         "Lebanon (‫لبنان‬‎)",
39175         "lb",
39176         "961"
39177       ],
39178       [
39179         "Lesotho",
39180         "ls",
39181         "266"
39182       ],
39183       [
39184         "Liberia",
39185         "lr",
39186         "231"
39187       ],
39188       [
39189         "Libya (‫ليبيا‬‎)",
39190         "ly",
39191         "218"
39192       ],
39193       [
39194         "Liechtenstein",
39195         "li",
39196         "423"
39197       ],
39198       [
39199         "Lithuania (Lietuva)",
39200         "lt",
39201         "370"
39202       ],
39203       [
39204         "Luxembourg",
39205         "lu",
39206         "352"
39207       ],
39208       [
39209         "Macau (澳門)",
39210         "mo",
39211         "853"
39212       ],
39213       [
39214         "Macedonia (FYROM) (Македонија)",
39215         "mk",
39216         "389"
39217       ],
39218       [
39219         "Madagascar (Madagasikara)",
39220         "mg",
39221         "261"
39222       ],
39223       [
39224         "Malawi",
39225         "mw",
39226         "265"
39227       ],
39228       [
39229         "Malaysia",
39230         "my",
39231         "60"
39232       ],
39233       [
39234         "Maldives",
39235         "mv",
39236         "960"
39237       ],
39238       [
39239         "Mali",
39240         "ml",
39241         "223"
39242       ],
39243       [
39244         "Malta",
39245         "mt",
39246         "356"
39247       ],
39248       [
39249         "Marshall Islands",
39250         "mh",
39251         "692"
39252       ],
39253       [
39254         "Martinique",
39255         "mq",
39256         "596"
39257       ],
39258       [
39259         "Mauritania (‫موريتانيا‬‎)",
39260         "mr",
39261         "222"
39262       ],
39263       [
39264         "Mauritius (Moris)",
39265         "mu",
39266         "230"
39267       ],
39268       [
39269         "Mayotte",
39270         "yt",
39271         "262",
39272         1
39273       ],
39274       [
39275         "Mexico (México)",
39276         "mx",
39277         "52"
39278       ],
39279       [
39280         "Micronesia",
39281         "fm",
39282         "691"
39283       ],
39284       [
39285         "Moldova (Republica Moldova)",
39286         "md",
39287         "373"
39288       ],
39289       [
39290         "Monaco",
39291         "mc",
39292         "377"
39293       ],
39294       [
39295         "Mongolia (Монгол)",
39296         "mn",
39297         "976"
39298       ],
39299       [
39300         "Montenegro (Crna Gora)",
39301         "me",
39302         "382"
39303       ],
39304       [
39305         "Montserrat",
39306         "ms",
39307         "1664"
39308       ],
39309       [
39310         "Morocco (‫المغرب‬‎)",
39311         "ma",
39312         "212",
39313         0
39314       ],
39315       [
39316         "Mozambique (Moçambique)",
39317         "mz",
39318         "258"
39319       ],
39320       [
39321         "Myanmar (Burma) (မြန်မာ)",
39322         "mm",
39323         "95"
39324       ],
39325       [
39326         "Namibia (Namibië)",
39327         "na",
39328         "264"
39329       ],
39330       [
39331         "Nauru",
39332         "nr",
39333         "674"
39334       ],
39335       [
39336         "Nepal (नेपाल)",
39337         "np",
39338         "977"
39339       ],
39340       [
39341         "Netherlands (Nederland)",
39342         "nl",
39343         "31"
39344       ],
39345       [
39346         "New Caledonia (Nouvelle-Calédonie)",
39347         "nc",
39348         "687"
39349       ],
39350       [
39351         "New Zealand",
39352         "nz",
39353         "64"
39354       ],
39355       [
39356         "Nicaragua",
39357         "ni",
39358         "505"
39359       ],
39360       [
39361         "Niger (Nijar)",
39362         "ne",
39363         "227"
39364       ],
39365       [
39366         "Nigeria",
39367         "ng",
39368         "234"
39369       ],
39370       [
39371         "Niue",
39372         "nu",
39373         "683"
39374       ],
39375       [
39376         "Norfolk Island",
39377         "nf",
39378         "672"
39379       ],
39380       [
39381         "North Korea (조선 민주주의 인민 공화국)",
39382         "kp",
39383         "850"
39384       ],
39385       [
39386         "Northern Mariana Islands",
39387         "mp",
39388         "1670"
39389       ],
39390       [
39391         "Norway (Norge)",
39392         "no",
39393         "47",
39394         0
39395       ],
39396       [
39397         "Oman (‫عُمان‬‎)",
39398         "om",
39399         "968"
39400       ],
39401       [
39402         "Pakistan (‫پاکستان‬‎)",
39403         "pk",
39404         "92"
39405       ],
39406       [
39407         "Palau",
39408         "pw",
39409         "680"
39410       ],
39411       [
39412         "Palestine (‫فلسطين‬‎)",
39413         "ps",
39414         "970"
39415       ],
39416       [
39417         "Panama (Panamá)",
39418         "pa",
39419         "507"
39420       ],
39421       [
39422         "Papua New Guinea",
39423         "pg",
39424         "675"
39425       ],
39426       [
39427         "Paraguay",
39428         "py",
39429         "595"
39430       ],
39431       [
39432         "Peru (Perú)",
39433         "pe",
39434         "51"
39435       ],
39436       [
39437         "Philippines",
39438         "ph",
39439         "63"
39440       ],
39441       [
39442         "Poland (Polska)",
39443         "pl",
39444         "48"
39445       ],
39446       [
39447         "Portugal",
39448         "pt",
39449         "351"
39450       ],
39451       [
39452         "Puerto Rico",
39453         "pr",
39454         "1",
39455         3,
39456         ["787", "939"]
39457       ],
39458       [
39459         "Qatar (‫قطر‬‎)",
39460         "qa",
39461         "974"
39462       ],
39463       [
39464         "Réunion (La Réunion)",
39465         "re",
39466         "262",
39467         0
39468       ],
39469       [
39470         "Romania (România)",
39471         "ro",
39472         "40"
39473       ],
39474       [
39475         "Russia (Россия)",
39476         "ru",
39477         "7",
39478         0
39479       ],
39480       [
39481         "Rwanda",
39482         "rw",
39483         "250"
39484       ],
39485       [
39486         "Saint Barthélemy",
39487         "bl",
39488         "590",
39489         1
39490       ],
39491       [
39492         "Saint Helena",
39493         "sh",
39494         "290"
39495       ],
39496       [
39497         "Saint Kitts and Nevis",
39498         "kn",
39499         "1869"
39500       ],
39501       [
39502         "Saint Lucia",
39503         "lc",
39504         "1758"
39505       ],
39506       [
39507         "Saint Martin (Saint-Martin (partie française))",
39508         "mf",
39509         "590",
39510         2
39511       ],
39512       [
39513         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39514         "pm",
39515         "508"
39516       ],
39517       [
39518         "Saint Vincent and the Grenadines",
39519         "vc",
39520         "1784"
39521       ],
39522       [
39523         "Samoa",
39524         "ws",
39525         "685"
39526       ],
39527       [
39528         "San Marino",
39529         "sm",
39530         "378"
39531       ],
39532       [
39533         "São Tomé and Príncipe (São Tomé e Príncipe)",
39534         "st",
39535         "239"
39536       ],
39537       [
39538         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39539         "sa",
39540         "966"
39541       ],
39542       [
39543         "Senegal (Sénégal)",
39544         "sn",
39545         "221"
39546       ],
39547       [
39548         "Serbia (Србија)",
39549         "rs",
39550         "381"
39551       ],
39552       [
39553         "Seychelles",
39554         "sc",
39555         "248"
39556       ],
39557       [
39558         "Sierra Leone",
39559         "sl",
39560         "232"
39561       ],
39562       [
39563         "Singapore",
39564         "sg",
39565         "65"
39566       ],
39567       [
39568         "Sint Maarten",
39569         "sx",
39570         "1721"
39571       ],
39572       [
39573         "Slovakia (Slovensko)",
39574         "sk",
39575         "421"
39576       ],
39577       [
39578         "Slovenia (Slovenija)",
39579         "si",
39580         "386"
39581       ],
39582       [
39583         "Solomon Islands",
39584         "sb",
39585         "677"
39586       ],
39587       [
39588         "Somalia (Soomaaliya)",
39589         "so",
39590         "252"
39591       ],
39592       [
39593         "South Africa",
39594         "za",
39595         "27"
39596       ],
39597       [
39598         "South Korea (대한민국)",
39599         "kr",
39600         "82"
39601       ],
39602       [
39603         "South Sudan (‫جنوب السودان‬‎)",
39604         "ss",
39605         "211"
39606       ],
39607       [
39608         "Spain (España)",
39609         "es",
39610         "34"
39611       ],
39612       [
39613         "Sri Lanka (ශ්‍රී ලංකාව)",
39614         "lk",
39615         "94"
39616       ],
39617       [
39618         "Sudan (‫السودان‬‎)",
39619         "sd",
39620         "249"
39621       ],
39622       [
39623         "Suriname",
39624         "sr",
39625         "597"
39626       ],
39627       [
39628         "Svalbard and Jan Mayen",
39629         "sj",
39630         "47",
39631         1
39632       ],
39633       [
39634         "Swaziland",
39635         "sz",
39636         "268"
39637       ],
39638       [
39639         "Sweden (Sverige)",
39640         "se",
39641         "46"
39642       ],
39643       [
39644         "Switzerland (Schweiz)",
39645         "ch",
39646         "41"
39647       ],
39648       [
39649         "Syria (‫سوريا‬‎)",
39650         "sy",
39651         "963"
39652       ],
39653       [
39654         "Taiwan (台灣)",
39655         "tw",
39656         "886"
39657       ],
39658       [
39659         "Tajikistan",
39660         "tj",
39661         "992"
39662       ],
39663       [
39664         "Tanzania",
39665         "tz",
39666         "255"
39667       ],
39668       [
39669         "Thailand (ไทย)",
39670         "th",
39671         "66"
39672       ],
39673       [
39674         "Timor-Leste",
39675         "tl",
39676         "670"
39677       ],
39678       [
39679         "Togo",
39680         "tg",
39681         "228"
39682       ],
39683       [
39684         "Tokelau",
39685         "tk",
39686         "690"
39687       ],
39688       [
39689         "Tonga",
39690         "to",
39691         "676"
39692       ],
39693       [
39694         "Trinidad and Tobago",
39695         "tt",
39696         "1868"
39697       ],
39698       [
39699         "Tunisia (‫تونس‬‎)",
39700         "tn",
39701         "216"
39702       ],
39703       [
39704         "Turkey (Türkiye)",
39705         "tr",
39706         "90"
39707       ],
39708       [
39709         "Turkmenistan",
39710         "tm",
39711         "993"
39712       ],
39713       [
39714         "Turks and Caicos Islands",
39715         "tc",
39716         "1649"
39717       ],
39718       [
39719         "Tuvalu",
39720         "tv",
39721         "688"
39722       ],
39723       [
39724         "U.S. Virgin Islands",
39725         "vi",
39726         "1340"
39727       ],
39728       [
39729         "Uganda",
39730         "ug",
39731         "256"
39732       ],
39733       [
39734         "Ukraine (Україна)",
39735         "ua",
39736         "380"
39737       ],
39738       [
39739         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39740         "ae",
39741         "971"
39742       ],
39743       [
39744         "United Kingdom",
39745         "gb",
39746         "44",
39747         0
39748       ],
39749       [
39750         "United States",
39751         "us",
39752         "1",
39753         0
39754       ],
39755       [
39756         "Uruguay",
39757         "uy",
39758         "598"
39759       ],
39760       [
39761         "Uzbekistan (Oʻzbekiston)",
39762         "uz",
39763         "998"
39764       ],
39765       [
39766         "Vanuatu",
39767         "vu",
39768         "678"
39769       ],
39770       [
39771         "Vatican City (Città del Vaticano)",
39772         "va",
39773         "39",
39774         1
39775       ],
39776       [
39777         "Venezuela",
39778         "ve",
39779         "58"
39780       ],
39781       [
39782         "Vietnam (Việt Nam)",
39783         "vn",
39784         "84"
39785       ],
39786       [
39787         "Wallis and Futuna (Wallis-et-Futuna)",
39788         "wf",
39789         "681"
39790       ],
39791       [
39792         "Western Sahara (‫الصحراء الغربية‬‎)",
39793         "eh",
39794         "212",
39795         1
39796       ],
39797       [
39798         "Yemen (‫اليمن‬‎)",
39799         "ye",
39800         "967"
39801       ],
39802       [
39803         "Zambia",
39804         "zm",
39805         "260"
39806       ],
39807       [
39808         "Zimbabwe",
39809         "zw",
39810         "263"
39811       ],
39812       [
39813         "Åland Islands",
39814         "ax",
39815         "358",
39816         1
39817       ]
39818   ];
39819   
39820   return d;
39821 }/**
39822 *    This script refer to:
39823 *    Title: International Telephone Input
39824 *    Author: Jack O'Connor
39825 *    Code version:  v12.1.12
39826 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39827 **/
39828
39829 /**
39830  * @class Roo.bootstrap.PhoneInput
39831  * @extends Roo.bootstrap.TriggerField
39832  * An input with International dial-code selection
39833  
39834  * @cfg {String} defaultDialCode default '+852'
39835  * @cfg {Array} preferedCountries default []
39836   
39837  * @constructor
39838  * Create a new PhoneInput.
39839  * @param {Object} config Configuration options
39840  */
39841
39842 Roo.bootstrap.PhoneInput = function(config) {
39843     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39844 };
39845
39846 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39847         
39848         listWidth: undefined,
39849         
39850         selectedClass: 'active',
39851         
39852         invalidClass : "has-warning",
39853         
39854         validClass: 'has-success',
39855         
39856         allowed: '0123456789',
39857         
39858         /**
39859          * @cfg {String} defaultDialCode The default dial code when initializing the input
39860          */
39861         defaultDialCode: '+852',
39862         
39863         /**
39864          * @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
39865          */
39866         preferedCountries: false,
39867         
39868         getAutoCreate : function()
39869         {
39870             var data = Roo.bootstrap.PhoneInputData();
39871             var align = this.labelAlign || this.parentLabelAlign();
39872             var id = Roo.id();
39873             
39874             this.allCountries = [];
39875             this.dialCodeMapping = [];
39876             
39877             for (var i = 0; i < data.length; i++) {
39878               var c = data[i];
39879               this.allCountries[i] = {
39880                 name: c[0],
39881                 iso2: c[1],
39882                 dialCode: c[2],
39883                 priority: c[3] || 0,
39884                 areaCodes: c[4] || null
39885               };
39886               this.dialCodeMapping[c[2]] = {
39887                   name: c[0],
39888                   iso2: c[1],
39889                   priority: c[3] || 0,
39890                   areaCodes: c[4] || null
39891               };
39892             }
39893             
39894             var cfg = {
39895                 cls: 'form-group',
39896                 cn: []
39897             };
39898             
39899             var input =  {
39900                 tag: 'input',
39901                 id : id,
39902                 cls : 'form-control tel-input',
39903                 autocomplete: 'new-password'
39904             };
39905             
39906             var hiddenInput = {
39907                 tag: 'input',
39908                 type: 'hidden',
39909                 cls: 'hidden-tel-input'
39910             };
39911             
39912             if (this.name) {
39913                 hiddenInput.name = this.name;
39914             }
39915             
39916             if (this.disabled) {
39917                 input.disabled = true;
39918             }
39919             
39920             var flag_container = {
39921                 tag: 'div',
39922                 cls: 'flag-box',
39923                 cn: [
39924                     {
39925                         tag: 'div',
39926                         cls: 'flag'
39927                     },
39928                     {
39929                         tag: 'div',
39930                         cls: 'caret'
39931                     }
39932                 ]
39933             };
39934             
39935             var box = {
39936                 tag: 'div',
39937                 cls: this.hasFeedback ? 'has-feedback' : '',
39938                 cn: [
39939                     hiddenInput,
39940                     input,
39941                     {
39942                         tag: 'input',
39943                         cls: 'dial-code-holder',
39944                         disabled: true
39945                     }
39946                 ]
39947             };
39948             
39949             var container = {
39950                 cls: 'roo-select2-container input-group',
39951                 cn: [
39952                     flag_container,
39953                     box
39954                 ]
39955             };
39956             
39957             if (this.fieldLabel.length) {
39958                 var indicator = {
39959                     tag: 'i',
39960                     tooltip: 'This field is required'
39961                 };
39962                 
39963                 var label = {
39964                     tag: 'label',
39965                     'for':  id,
39966                     cls: 'control-label',
39967                     cn: []
39968                 };
39969                 
39970                 var label_text = {
39971                     tag: 'span',
39972                     html: this.fieldLabel
39973                 };
39974                 
39975                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39976                 label.cn = [
39977                     indicator,
39978                     label_text
39979                 ];
39980                 
39981                 if(this.indicatorpos == 'right') {
39982                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39983                     label.cn = [
39984                         label_text,
39985                         indicator
39986                     ];
39987                 }
39988                 
39989                 if(align == 'left') {
39990                     container = {
39991                         tag: 'div',
39992                         cn: [
39993                             container
39994                         ]
39995                     };
39996                     
39997                     if(this.labelWidth > 12){
39998                         label.style = "width: " + this.labelWidth + 'px';
39999                     }
40000                     if(this.labelWidth < 13 && this.labelmd == 0){
40001                         this.labelmd = this.labelWidth;
40002                     }
40003                     if(this.labellg > 0){
40004                         label.cls += ' col-lg-' + this.labellg;
40005                         input.cls += ' col-lg-' + (12 - this.labellg);
40006                     }
40007                     if(this.labelmd > 0){
40008                         label.cls += ' col-md-' + this.labelmd;
40009                         container.cls += ' col-md-' + (12 - this.labelmd);
40010                     }
40011                     if(this.labelsm > 0){
40012                         label.cls += ' col-sm-' + this.labelsm;
40013                         container.cls += ' col-sm-' + (12 - this.labelsm);
40014                     }
40015                     if(this.labelxs > 0){
40016                         label.cls += ' col-xs-' + this.labelxs;
40017                         container.cls += ' col-xs-' + (12 - this.labelxs);
40018                     }
40019                 }
40020             }
40021             
40022             cfg.cn = [
40023                 label,
40024                 container
40025             ];
40026             
40027             var settings = this;
40028             
40029             ['xs','sm','md','lg'].map(function(size){
40030                 if (settings[size]) {
40031                     cfg.cls += ' col-' + size + '-' + settings[size];
40032                 }
40033             });
40034             
40035             this.store = new Roo.data.Store({
40036                 proxy : new Roo.data.MemoryProxy({}),
40037                 reader : new Roo.data.JsonReader({
40038                     fields : [
40039                         {
40040                             'name' : 'name',
40041                             'type' : 'string'
40042                         },
40043                         {
40044                             'name' : 'iso2',
40045                             'type' : 'string'
40046                         },
40047                         {
40048                             'name' : 'dialCode',
40049                             'type' : 'string'
40050                         },
40051                         {
40052                             'name' : 'priority',
40053                             'type' : 'string'
40054                         },
40055                         {
40056                             'name' : 'areaCodes',
40057                             'type' : 'string'
40058                         }
40059                     ]
40060                 })
40061             });
40062             
40063             if(!this.preferedCountries) {
40064                 this.preferedCountries = [
40065                     'hk',
40066                     'gb',
40067                     'us'
40068                 ];
40069             }
40070             
40071             var p = this.preferedCountries.reverse();
40072             
40073             if(p) {
40074                 for (var i = 0; i < p.length; i++) {
40075                     for (var j = 0; j < this.allCountries.length; j++) {
40076                         if(this.allCountries[j].iso2 == p[i]) {
40077                             var t = this.allCountries[j];
40078                             this.allCountries.splice(j,1);
40079                             this.allCountries.unshift(t);
40080                         }
40081                     } 
40082                 }
40083             }
40084             
40085             this.store.proxy.data = {
40086                 success: true,
40087                 data: this.allCountries
40088             };
40089             
40090             return cfg;
40091         },
40092         
40093         initEvents : function()
40094         {
40095             this.createList();
40096             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40097             
40098             this.indicator = this.indicatorEl();
40099             this.flag = this.flagEl();
40100             this.dialCodeHolder = this.dialCodeHolderEl();
40101             
40102             this.trigger = this.el.select('div.flag-box',true).first();
40103             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40104             
40105             var _this = this;
40106             
40107             (function(){
40108                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40109                 _this.list.setWidth(lw);
40110             }).defer(100);
40111             
40112             this.list.on('mouseover', this.onViewOver, this);
40113             this.list.on('mousemove', this.onViewMove, this);
40114             this.inputEl().on("keyup", this.onKeyUp, this);
40115             
40116             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40117
40118             this.view = new Roo.View(this.list, this.tpl, {
40119                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40120             });
40121             
40122             this.view.on('click', this.onViewClick, this);
40123             this.setValue(this.defaultDialCode);
40124         },
40125         
40126         onTriggerClick : function(e)
40127         {
40128             Roo.log('trigger click');
40129             if(this.disabled){
40130                 return;
40131             }
40132             
40133             if(this.isExpanded()){
40134                 this.collapse();
40135                 this.hasFocus = false;
40136             }else {
40137                 this.store.load({});
40138                 this.hasFocus = true;
40139                 this.expand();
40140             }
40141         },
40142         
40143         isExpanded : function()
40144         {
40145             return this.list.isVisible();
40146         },
40147         
40148         collapse : function()
40149         {
40150             if(!this.isExpanded()){
40151                 return;
40152             }
40153             this.list.hide();
40154             Roo.get(document).un('mousedown', this.collapseIf, this);
40155             Roo.get(document).un('mousewheel', this.collapseIf, this);
40156             this.fireEvent('collapse', this);
40157             this.validate();
40158         },
40159         
40160         expand : function()
40161         {
40162             Roo.log('expand');
40163
40164             if(this.isExpanded() || !this.hasFocus){
40165                 return;
40166             }
40167             
40168             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40169             this.list.setWidth(lw);
40170             
40171             this.list.show();
40172             this.restrictHeight();
40173             
40174             Roo.get(document).on('mousedown', this.collapseIf, this);
40175             Roo.get(document).on('mousewheel', this.collapseIf, this);
40176             
40177             this.fireEvent('expand', this);
40178         },
40179         
40180         restrictHeight : function()
40181         {
40182             this.list.alignTo(this.inputEl(), this.listAlign);
40183             this.list.alignTo(this.inputEl(), this.listAlign);
40184         },
40185         
40186         onViewOver : function(e, t)
40187         {
40188             if(this.inKeyMode){
40189                 return;
40190             }
40191             var item = this.view.findItemFromChild(t);
40192             
40193             if(item){
40194                 var index = this.view.indexOf(item);
40195                 this.select(index, false);
40196             }
40197         },
40198
40199         // private
40200         onViewClick : function(view, doFocus, el, e)
40201         {
40202             var index = this.view.getSelectedIndexes()[0];
40203             
40204             var r = this.store.getAt(index);
40205             
40206             if(r){
40207                 this.onSelect(r, index);
40208             }
40209             if(doFocus !== false && !this.blockFocus){
40210                 this.inputEl().focus();
40211             }
40212         },
40213         
40214         onViewMove : function(e, t)
40215         {
40216             this.inKeyMode = false;
40217         },
40218         
40219         select : function(index, scrollIntoView)
40220         {
40221             this.selectedIndex = index;
40222             this.view.select(index);
40223             if(scrollIntoView !== false){
40224                 var el = this.view.getNode(index);
40225                 if(el){
40226                     this.list.scrollChildIntoView(el, false);
40227                 }
40228             }
40229         },
40230         
40231         createList : function()
40232         {
40233             this.list = Roo.get(document.body).createChild({
40234                 tag: 'ul',
40235                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40236                 style: 'display:none'
40237             });
40238             
40239             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40240         },
40241         
40242         collapseIf : function(e)
40243         {
40244             var in_combo  = e.within(this.el);
40245             var in_list =  e.within(this.list);
40246             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40247             
40248             if (in_combo || in_list || is_list) {
40249                 return;
40250             }
40251             this.collapse();
40252         },
40253         
40254         onSelect : function(record, index)
40255         {
40256             if(this.fireEvent('beforeselect', this, record, index) !== false){
40257                 
40258                 this.setFlagClass(record.data.iso2);
40259                 this.setDialCode(record.data.dialCode);
40260                 this.hasFocus = false;
40261                 this.collapse();
40262                 this.fireEvent('select', this, record, index);
40263             }
40264         },
40265         
40266         flagEl : function()
40267         {
40268             var flag = this.el.select('div.flag',true).first();
40269             if(!flag){
40270                 return false;
40271             }
40272             return flag;
40273         },
40274         
40275         dialCodeHolderEl : function()
40276         {
40277             var d = this.el.select('input.dial-code-holder',true).first();
40278             if(!d){
40279                 return false;
40280             }
40281             return d;
40282         },
40283         
40284         setDialCode : function(v)
40285         {
40286             this.dialCodeHolder.dom.value = '+'+v;
40287         },
40288         
40289         setFlagClass : function(n)
40290         {
40291             this.flag.dom.className = 'flag '+n;
40292         },
40293         
40294         getValue : function()
40295         {
40296             var v = this.inputEl().getValue();
40297             if(this.dialCodeHolder) {
40298                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40299             }
40300             return v;
40301         },
40302         
40303         setValue : function(v)
40304         {
40305             var d = this.getDialCode(v);
40306             
40307             //invalid dial code
40308             if(v.length == 0 || !d || d.length == 0) {
40309                 if(this.rendered){
40310                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40311                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40312                 }
40313                 return;
40314             }
40315             
40316             //valid dial code
40317             this.setFlagClass(this.dialCodeMapping[d].iso2);
40318             this.setDialCode(d);
40319             this.inputEl().dom.value = v.replace('+'+d,'');
40320             this.hiddenEl().dom.value = this.getValue();
40321             
40322             this.validate();
40323         },
40324         
40325         getDialCode : function(v)
40326         {
40327             v = v ||  '';
40328             
40329             if (v.length == 0) {
40330                 return this.dialCodeHolder.dom.value;
40331             }
40332             
40333             var dialCode = "";
40334             if (v.charAt(0) != "+") {
40335                 return false;
40336             }
40337             var numericChars = "";
40338             for (var i = 1; i < v.length; i++) {
40339               var c = v.charAt(i);
40340               if (!isNaN(c)) {
40341                 numericChars += c;
40342                 if (this.dialCodeMapping[numericChars]) {
40343                   dialCode = v.substr(1, i);
40344                 }
40345                 if (numericChars.length == 4) {
40346                   break;
40347                 }
40348               }
40349             }
40350             return dialCode;
40351         },
40352         
40353         reset : function()
40354         {
40355             this.setValue(this.defaultDialCode);
40356             this.validate();
40357         },
40358         
40359         hiddenEl : function()
40360         {
40361             return this.el.select('input.hidden-tel-input',true).first();
40362         },
40363         
40364         onKeyUp : function(e){
40365             
40366             var k = e.getKey();
40367             var c = e.getCharCode();
40368             
40369             if(
40370                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40371                     this.allowed.indexOf(String.fromCharCode(c)) === -1
40372             ){
40373                 e.stopEvent();
40374             }
40375             
40376             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40377             //     return;
40378             // }
40379             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40380                 e.stopEvent();
40381             }
40382             
40383             this.setValue(this.getValue());
40384         }
40385         
40386 });
40387 /**
40388  * @class Roo.bootstrap.MoneyField
40389  * @extends Roo.bootstrap.ComboBox
40390  * Bootstrap MoneyField class
40391  * 
40392  * @constructor
40393  * Create a new MoneyField.
40394  * @param {Object} config Configuration options
40395  */
40396
40397 Roo.bootstrap.MoneyField = function(config) {
40398     
40399     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40400     
40401 };
40402
40403 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40404     
40405     /**
40406      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40407      */
40408     allowDecimals : true,
40409     /**
40410      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40411      */
40412     decimalSeparator : ".",
40413     /**
40414      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40415      */
40416     decimalPrecision : 0,
40417     /**
40418      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40419      */
40420     allowNegative : true,
40421     /**
40422      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40423      */
40424     allowZero: true,
40425     /**
40426      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40427      */
40428     minValue : Number.NEGATIVE_INFINITY,
40429     /**
40430      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40431      */
40432     maxValue : Number.MAX_VALUE,
40433     /**
40434      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40435      */
40436     minText : "The minimum value for this field is {0}",
40437     /**
40438      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40439      */
40440     maxText : "The maximum value for this field is {0}",
40441     /**
40442      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40443      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40444      */
40445     nanText : "{0} is not a valid number",
40446     /**
40447      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40448      */
40449     castInt : true,
40450     /**
40451      * @cfg {String} defaults currency of the MoneyField
40452      * value should be in lkey
40453      */
40454     defaultCurrency : false,
40455     /**
40456      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40457      */
40458     thousandsDelimiter : false,
40459     
40460     
40461     inputlg : 9,
40462     inputmd : 9,
40463     inputsm : 9,
40464     inputxs : 6,
40465     
40466     store : false,
40467     
40468     getAutoCreate : function()
40469     {
40470         var align = this.labelAlign || this.parentLabelAlign();
40471         
40472         var id = Roo.id();
40473
40474         var cfg = {
40475             cls: 'form-group',
40476             cn: []
40477         };
40478
40479         var input =  {
40480             tag: 'input',
40481             id : id,
40482             cls : 'form-control roo-money-amount-input',
40483             autocomplete: 'new-password'
40484         };
40485         
40486         var hiddenInput = {
40487             tag: 'input',
40488             type: 'hidden',
40489             id: Roo.id(),
40490             cls: 'hidden-number-input'
40491         };
40492         
40493         if (this.name) {
40494             hiddenInput.name = this.name;
40495         }
40496
40497         if (this.disabled) {
40498             input.disabled = true;
40499         }
40500
40501         var clg = 12 - this.inputlg;
40502         var cmd = 12 - this.inputmd;
40503         var csm = 12 - this.inputsm;
40504         var cxs = 12 - this.inputxs;
40505         
40506         var container = {
40507             tag : 'div',
40508             cls : 'row roo-money-field',
40509             cn : [
40510                 {
40511                     tag : 'div',
40512                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40513                     cn : [
40514                         {
40515                             tag : 'div',
40516                             cls: 'roo-select2-container input-group',
40517                             cn: [
40518                                 {
40519                                     tag : 'input',
40520                                     cls : 'form-control roo-money-currency-input',
40521                                     autocomplete: 'new-password',
40522                                     readOnly : 1,
40523                                     name : this.currencyName
40524                                 },
40525                                 {
40526                                     tag :'span',
40527                                     cls : 'input-group-addon',
40528                                     cn : [
40529                                         {
40530                                             tag: 'span',
40531                                             cls: 'caret'
40532                                         }
40533                                     ]
40534                                 }
40535                             ]
40536                         }
40537                     ]
40538                 },
40539                 {
40540                     tag : 'div',
40541                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40542                     cn : [
40543                         {
40544                             tag: 'div',
40545                             cls: this.hasFeedback ? 'has-feedback' : '',
40546                             cn: [
40547                                 input
40548                             ]
40549                         }
40550                     ]
40551                 }
40552             ]
40553             
40554         };
40555         
40556         if (this.fieldLabel.length) {
40557             var indicator = {
40558                 tag: 'i',
40559                 tooltip: 'This field is required'
40560             };
40561
40562             var label = {
40563                 tag: 'label',
40564                 'for':  id,
40565                 cls: 'control-label',
40566                 cn: []
40567             };
40568
40569             var label_text = {
40570                 tag: 'span',
40571                 html: this.fieldLabel
40572             };
40573
40574             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40575             label.cn = [
40576                 indicator,
40577                 label_text
40578             ];
40579
40580             if(this.indicatorpos == 'right') {
40581                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40582                 label.cn = [
40583                     label_text,
40584                     indicator
40585                 ];
40586             }
40587
40588             if(align == 'left') {
40589                 container = {
40590                     tag: 'div',
40591                     cn: [
40592                         container
40593                     ]
40594                 };
40595
40596                 if(this.labelWidth > 12){
40597                     label.style = "width: " + this.labelWidth + 'px';
40598                 }
40599                 if(this.labelWidth < 13 && this.labelmd == 0){
40600                     this.labelmd = this.labelWidth;
40601                 }
40602                 if(this.labellg > 0){
40603                     label.cls += ' col-lg-' + this.labellg;
40604                     input.cls += ' col-lg-' + (12 - this.labellg);
40605                 }
40606                 if(this.labelmd > 0){
40607                     label.cls += ' col-md-' + this.labelmd;
40608                     container.cls += ' col-md-' + (12 - this.labelmd);
40609                 }
40610                 if(this.labelsm > 0){
40611                     label.cls += ' col-sm-' + this.labelsm;
40612                     container.cls += ' col-sm-' + (12 - this.labelsm);
40613                 }
40614                 if(this.labelxs > 0){
40615                     label.cls += ' col-xs-' + this.labelxs;
40616                     container.cls += ' col-xs-' + (12 - this.labelxs);
40617                 }
40618             }
40619         }
40620
40621         cfg.cn = [
40622             label,
40623             container,
40624             hiddenInput
40625         ];
40626         
40627         var settings = this;
40628
40629         ['xs','sm','md','lg'].map(function(size){
40630             if (settings[size]) {
40631                 cfg.cls += ' col-' + size + '-' + settings[size];
40632             }
40633         });
40634         
40635         return cfg;
40636     },
40637     
40638     initEvents : function()
40639     {
40640         this.indicator = this.indicatorEl();
40641         
40642         this.initCurrencyEvent();
40643         
40644         this.initNumberEvent();
40645     },
40646     
40647     initCurrencyEvent : function()
40648     {
40649         if (!this.store) {
40650             throw "can not find store for combo";
40651         }
40652         
40653         this.store = Roo.factory(this.store, Roo.data);
40654         this.store.parent = this;
40655         
40656         this.createList();
40657         
40658         this.triggerEl = this.el.select('.input-group-addon', true).first();
40659         
40660         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40661         
40662         var _this = this;
40663         
40664         (function(){
40665             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40666             _this.list.setWidth(lw);
40667         }).defer(100);
40668         
40669         this.list.on('mouseover', this.onViewOver, this);
40670         this.list.on('mousemove', this.onViewMove, this);
40671         this.list.on('scroll', this.onViewScroll, this);
40672         
40673         if(!this.tpl){
40674             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40675         }
40676         
40677         this.view = new Roo.View(this.list, this.tpl, {
40678             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40679         });
40680         
40681         this.view.on('click', this.onViewClick, this);
40682         
40683         this.store.on('beforeload', this.onBeforeLoad, this);
40684         this.store.on('load', this.onLoad, this);
40685         this.store.on('loadexception', this.onLoadException, this);
40686         
40687         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40688             "up" : function(e){
40689                 this.inKeyMode = true;
40690                 this.selectPrev();
40691             },
40692
40693             "down" : function(e){
40694                 if(!this.isExpanded()){
40695                     this.onTriggerClick();
40696                 }else{
40697                     this.inKeyMode = true;
40698                     this.selectNext();
40699                 }
40700             },
40701
40702             "enter" : function(e){
40703                 this.collapse();
40704                 
40705                 if(this.fireEvent("specialkey", this, e)){
40706                     this.onViewClick(false);
40707                 }
40708                 
40709                 return true;
40710             },
40711
40712             "esc" : function(e){
40713                 this.collapse();
40714             },
40715
40716             "tab" : function(e){
40717                 this.collapse();
40718                 
40719                 if(this.fireEvent("specialkey", this, e)){
40720                     this.onViewClick(false);
40721                 }
40722                 
40723                 return true;
40724             },
40725
40726             scope : this,
40727
40728             doRelay : function(foo, bar, hname){
40729                 if(hname == 'down' || this.scope.isExpanded()){
40730                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40731                 }
40732                 return true;
40733             },
40734
40735             forceKeyDown: true
40736         });
40737         
40738         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40739         
40740     },
40741     
40742     initNumberEvent : function(e)
40743     {
40744         this.inputEl().on("keydown" , this.fireKey,  this);
40745         this.inputEl().on("focus", this.onFocus,  this);
40746         this.inputEl().on("blur", this.onBlur,  this);
40747         
40748         this.inputEl().relayEvent('keyup', this);
40749         
40750         if(this.indicator){
40751             this.indicator.addClass('invisible');
40752         }
40753  
40754         this.originalValue = this.getValue();
40755         
40756         if(this.validationEvent == 'keyup'){
40757             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40758             this.inputEl().on('keyup', this.filterValidation, this);
40759         }
40760         else if(this.validationEvent !== false){
40761             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40762         }
40763         
40764         if(this.selectOnFocus){
40765             this.on("focus", this.preFocus, this);
40766             
40767         }
40768         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40769             this.inputEl().on("keypress", this.filterKeys, this);
40770         } else {
40771             this.inputEl().relayEvent('keypress', this);
40772         }
40773         
40774         var allowed = "0123456789";
40775         
40776         if(this.allowDecimals){
40777             allowed += this.decimalSeparator;
40778         }
40779         
40780         if(this.allowNegative){
40781             allowed += "-";
40782         }
40783         
40784         if(this.thousandsDelimiter) {
40785             allowed += ",";
40786         }
40787         
40788         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40789         
40790         var keyPress = function(e){
40791             
40792             var k = e.getKey();
40793             
40794             var c = e.getCharCode();
40795             
40796             if(
40797                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40798                     allowed.indexOf(String.fromCharCode(c)) === -1
40799             ){
40800                 e.stopEvent();
40801                 return;
40802             }
40803             
40804             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40805                 return;
40806             }
40807             
40808             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40809                 e.stopEvent();
40810             }
40811         };
40812         
40813         this.inputEl().on("keypress", keyPress, this);
40814         
40815     },
40816     
40817     onTriggerClick : function(e)
40818     {   
40819         if(this.disabled){
40820             return;
40821         }
40822         
40823         this.page = 0;
40824         this.loadNext = false;
40825         
40826         if(this.isExpanded()){
40827             this.collapse();
40828             return;
40829         }
40830         
40831         this.hasFocus = true;
40832         
40833         if(this.triggerAction == 'all') {
40834             this.doQuery(this.allQuery, true);
40835             return;
40836         }
40837         
40838         this.doQuery(this.getRawValue());
40839     },
40840     
40841     getCurrency : function()
40842     {   
40843         var v = this.currencyEl().getValue();
40844         
40845         return v;
40846     },
40847     
40848     restrictHeight : function()
40849     {
40850         this.list.alignTo(this.currencyEl(), this.listAlign);
40851         this.list.alignTo(this.currencyEl(), this.listAlign);
40852     },
40853     
40854     onViewClick : function(view, doFocus, el, e)
40855     {
40856         var index = this.view.getSelectedIndexes()[0];
40857         
40858         var r = this.store.getAt(index);
40859         
40860         if(r){
40861             this.onSelect(r, index);
40862         }
40863     },
40864     
40865     onSelect : function(record, index){
40866         
40867         if(this.fireEvent('beforeselect', this, record, index) !== false){
40868         
40869             this.setFromCurrencyData(index > -1 ? record.data : false);
40870             
40871             this.collapse();
40872             
40873             this.fireEvent('select', this, record, index);
40874         }
40875     },
40876     
40877     setFromCurrencyData : function(o)
40878     {
40879         var currency = '';
40880         
40881         this.lastCurrency = o;
40882         
40883         if (this.currencyField) {
40884             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40885         } else {
40886             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40887         }
40888         
40889         this.lastSelectionText = currency;
40890         
40891         //setting default currency
40892         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40893             this.setCurrency(this.defaultCurrency);
40894             return;
40895         }
40896         
40897         this.setCurrency(currency);
40898     },
40899     
40900     setFromData : function(o)
40901     {
40902         var c = {};
40903         
40904         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40905         
40906         this.setFromCurrencyData(c);
40907         
40908         var value = '';
40909         
40910         if (this.name) {
40911             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40912         } else {
40913             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40914         }
40915         
40916         this.setValue(value);
40917         
40918     },
40919     
40920     setCurrency : function(v)
40921     {   
40922         this.currencyValue = v;
40923         
40924         if(this.rendered){
40925             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40926             this.validate();
40927         }
40928     },
40929     
40930     setValue : function(v)
40931     {
40932         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40933         
40934         this.value = v;
40935         
40936         if(this.rendered){
40937             
40938             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40939             
40940             this.inputEl().dom.value = (v == '') ? '' :
40941                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40942             
40943             if(!this.allowZero && v === '0') {
40944                 this.hiddenEl().dom.value = '';
40945                 this.inputEl().dom.value = '';
40946             }
40947             
40948             this.validate();
40949         }
40950     },
40951     
40952     getRawValue : function()
40953     {
40954         var v = this.inputEl().getValue();
40955         
40956         return v;
40957     },
40958     
40959     getValue : function()
40960     {
40961         return this.fixPrecision(this.parseValue(this.getRawValue()));
40962     },
40963     
40964     parseValue : function(value)
40965     {
40966         if(this.thousandsDelimiter) {
40967             value += "";
40968             r = new RegExp(",", "g");
40969             value = value.replace(r, "");
40970         }
40971         
40972         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40973         return isNaN(value) ? '' : value;
40974         
40975     },
40976     
40977     fixPrecision : function(value)
40978     {
40979         if(this.thousandsDelimiter) {
40980             value += "";
40981             r = new RegExp(",", "g");
40982             value = value.replace(r, "");
40983         }
40984         
40985         var nan = isNaN(value);
40986         
40987         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40988             return nan ? '' : value;
40989         }
40990         return parseFloat(value).toFixed(this.decimalPrecision);
40991     },
40992     
40993     decimalPrecisionFcn : function(v)
40994     {
40995         return Math.floor(v);
40996     },
40997     
40998     validateValue : function(value)
40999     {
41000         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41001             return false;
41002         }
41003         
41004         var num = this.parseValue(value);
41005         
41006         if(isNaN(num)){
41007             this.markInvalid(String.format(this.nanText, value));
41008             return false;
41009         }
41010         
41011         if(num < this.minValue){
41012             this.markInvalid(String.format(this.minText, this.minValue));
41013             return false;
41014         }
41015         
41016         if(num > this.maxValue){
41017             this.markInvalid(String.format(this.maxText, this.maxValue));
41018             return false;
41019         }
41020         
41021         return true;
41022     },
41023     
41024     validate : function()
41025     {
41026         if(this.disabled || this.allowBlank){
41027             this.markValid();
41028             return true;
41029         }
41030         
41031         var currency = this.getCurrency();
41032         
41033         if(this.validateValue(this.getRawValue()) && currency.length){
41034             this.markValid();
41035             return true;
41036         }
41037         
41038         this.markInvalid();
41039         return false;
41040     },
41041     
41042     getName: function()
41043     {
41044         return this.name;
41045     },
41046     
41047     beforeBlur : function()
41048     {
41049         if(!this.castInt){
41050             return;
41051         }
41052         
41053         var v = this.parseValue(this.getRawValue());
41054         
41055         if(v || v == 0){
41056             this.setValue(v);
41057         }
41058     },
41059     
41060     onBlur : function()
41061     {
41062         this.beforeBlur();
41063         
41064         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41065             //this.el.removeClass(this.focusClass);
41066         }
41067         
41068         this.hasFocus = false;
41069         
41070         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41071             this.validate();
41072         }
41073         
41074         var v = this.getValue();
41075         
41076         if(String(v) !== String(this.startValue)){
41077             this.fireEvent('change', this, v, this.startValue);
41078         }
41079         
41080         this.fireEvent("blur", this);
41081     },
41082     
41083     inputEl : function()
41084     {
41085         return this.el.select('.roo-money-amount-input', true).first();
41086     },
41087     
41088     currencyEl : function()
41089     {
41090         return this.el.select('.roo-money-currency-input', true).first();
41091     },
41092     
41093     hiddenEl : function()
41094     {
41095         return this.el.select('input.hidden-number-input',true).first();
41096     }
41097     
41098 });